From 80fe3d732a2295e92914f4b29884b5e5a89aa574 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 16 Nov 2021 18:04:37 +0400 Subject: [PATCH 001/180] Try to fix snap store upload --- snap/snapcraft.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 2f3e2ddd7..49babc948 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -149,6 +149,8 @@ parts: rm -rf "$SNAPCRAFT_PART_INSTALL/usr/share/icons" stage: - -./usr/lib/$SNAPCRAFT_ARCH_TRIPLET/libjpeg.so.8.2.2 + prime: + - -./lib/systemd after: - desktop-qt - extra-cmake-modules From a02642b91773e23b6db229b523aca64f7c68143d Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 10 Nov 2021 11:51:35 +0400 Subject: [PATCH 002/180] Switch to devtoolset-10 in Docker. --- Telegram/build/docker/build.sh | 2 +- Telegram/build/docker/centos_env/Dockerfile | 6 +++--- Telegram/build/docker/centos_env/build.sh | 2 +- Telegram/build/docker/centos_env/run.sh | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Telegram/build/docker/build.sh b/Telegram/build/docker/build.sh index 3c8e19e6a..54b00360b 100755 --- a/Telegram/build/docker/build.sh +++ b/Telegram/build/docker/build.sh @@ -16,7 +16,7 @@ if [ ! -d "$FullScriptPath/../../../../DesktopPrivate" ]; then fi Run () { - scl enable llvm-toolset-7.0 -- scl enable devtoolset-9 -- "$@" + scl enable llvm-toolset-7.0 -- scl enable devtoolset-10 -- "$@" } HomePath="$FullScriptPath/../.." diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 3d33100d3..239eda02b 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -28,11 +28,11 @@ RUN yum -y install git meson ninja-build autoconf automake libtool \ fontconfig-devel freetype-devel libX11-devel at-spi2-core-devel alsa-lib-devel \ pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel libudev-devel \ webkitgtk4-devel pkgconfig bison yasm file which xorg-x11-util-macros \ - devtoolset-9-make devtoolset-9-gcc devtoolset-9-gcc-c++ \ - devtoolset-9-binutils llvm-toolset-7.0 llvm-toolset-7.0-clang-devel \ + devtoolset-10-make devtoolset-10-gcc devtoolset-10-gcc-c++ \ + devtoolset-10-binutils llvm-toolset-7.0 llvm-toolset-7.0-clang-devel \ llvm-toolset-7.0-llvm-devel perl-XML-Parser && yum clean all -SHELL [ "bash", "-c", ". /opt/rh/devtoolset-9/enable; exec bash -c \"$@\"", "-s"] +SHELL [ "bash", "-c", ". /opt/rh/devtoolset-10/enable; exec bash -c \"$@\"", "-s"] ENV LibrariesPath /usr/src/Libraries ENV HFLAGS "-fstack-protector-all -fstack-clash-protection -fPIC -D_FORTIFY_SOURCE=2" diff --git a/Telegram/build/docker/centos_env/build.sh b/Telegram/build/docker/centos_env/build.sh index 7eebb7fb7..28f000202 100755 --- a/Telegram/build/docker/centos_env/build.sh +++ b/Telegram/build/docker/centos_env/build.sh @@ -1,4 +1,4 @@ -#!/usr/bin/scl enable llvm-toolset-7.0 -- scl enable devtoolset-9 -- bash +#!/usr/bin/scl enable llvm-toolset-7.0 -- scl enable devtoolset-10 -- bash cd Telegram ./configure.sh "$@" diff --git a/Telegram/build/docker/centos_env/run.sh b/Telegram/build/docker/centos_env/run.sh index 4d1451f7a..3df128ded 100755 --- a/Telegram/build/docker/centos_env/run.sh +++ b/Telegram/build/docker/centos_env/run.sh @@ -15,7 +15,7 @@ fi Command="$1" if [ "$Command" == "" ]; then - Command="scl enable llvm-toolset-7.0 -- scl enable devtoolset-9 -- bash" + Command="scl enable llvm-toolset-7.0 -- scl enable devtoolset-10 -- bash" fi docker run -it --rm --cpus=8 --memory=22g -v $HOME/Telegram/DesktopPrivate:/usr/src/DesktopPrivate -v $HOME/Telegram/tdesktop:/usr/src/tdesktop tdesktop:centos_env $Command From 96a3c704d273f2b7f7f7647be5fa1691fea1bd2a Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 14 Nov 2021 11:31:54 +0400 Subject: [PATCH 003/180] Use Ninja Multi-Config generator on Linux. --- Telegram/CMakeLists.txt | 9 +++++---- Telegram/build/docker/build.sh | 2 +- Telegram/configure.py | 7 +------ cmake | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 44e5d26d4..dc71724c2 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1399,12 +1399,13 @@ PRIVATE G_LOG_DOMAIN="Telegram" ) -if (APPLE OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL "" OR NOT "${output_name}" STREQUAL "Telegram") +if ("${CMAKE_GENERATOR}" STREQUAL "Xcode" + OR "${CMAKE_GENERATOR}" STREQUAL "Ninja Multi-Config" + OR NOT CMAKE_EXECUTABLE_SUFFIX STREQUAL "" + OR NOT "${output_name}" STREQUAL "Telegram") set(output_folder ${CMAKE_BINARY_DIR}) -elseif (DESKTOP_APP_SPECIAL_TARGET STREQUAL "") - set(output_folder ${CMAKE_BINARY_DIR}/bin) else() - set(output_folder ${CMAKE_BINARY_DIR}/$,Debug,Release>) + set(output_folder ${CMAKE_BINARY_DIR}/bin) endif() set_target_properties(Telegram PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_folder}) diff --git a/Telegram/build/docker/build.sh b/Telegram/build/docker/build.sh index 54b00360b..a85e4894f 100755 --- a/Telegram/build/docker/build.sh +++ b/Telegram/build/docker/build.sh @@ -33,7 +33,7 @@ fi Run ./configure.sh cd $ProjectPath -Run cmake --build . --config Release --target Telegram -- -j8 +Run cmake --build . --config Release --target Telegram cd $ReleasePath echo "$BinaryName build complete!" diff --git a/Telegram/configure.py b/Telegram/configure.py index 5f4349132..22de084a5 100644 --- a/Telegram/configure.py +++ b/Telegram/configure.py @@ -59,9 +59,4 @@ if officialTarget != '': arguments.append('-DTDESKTOP_API_HASH=' + apiHashMatch.group(1)) if arch != '': arguments.append(arch) - finish(run_cmake.run(scriptName, arguments)) -elif 'linux' in sys.platform: - debugCode = run_cmake.run(scriptName, arguments, "Debug") - finish(debugCode if debugCode else run_cmake.run(scriptName, arguments, "Release")) -else: - finish(run_cmake.run(scriptName, arguments)) +finish(run_cmake.run(scriptName, arguments)) diff --git a/cmake b/cmake index 051f7ba4e..2034686f0 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 051f7ba4e38226797bafbd9c1ed52ef38afe28b9 +Subproject commit 2034686f01d2207bde5b060e71a416f2f4419287 From 12eda0bb2619f426dda80361efd0437f41ee6312 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 14 Nov 2021 11:32:14 +0400 Subject: [PATCH 004/180] Use C++20 on Linux with GCC 10. --- Telegram/ThirdParty/libtgvoip | 2 +- Telegram/build/docker/centos_env/Dockerfile | 26 ++++----------------- Telegram/lib_ui | 2 +- cmake | 2 +- 4 files changed, 8 insertions(+), 24 deletions(-) diff --git a/Telegram/ThirdParty/libtgvoip b/Telegram/ThirdParty/libtgvoip index 373e41668..7efc43006 160000 --- a/Telegram/ThirdParty/libtgvoip +++ b/Telegram/ThirdParty/libtgvoip @@ -1 +1 @@ -Subproject commit 373e41668b265864f8976b83bb66dd6e9a583915 +Subproject commit 7efc430061aa37945fde7325439236526084c598 diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 239eda02b..953b827a6 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -801,38 +801,22 @@ FROM patches AS breakpad RUN git clone https://chromium.googlesource.com/breakpad/breakpad.git WORKDIR breakpad -RUN git checkout bc8fb886 +RUN git checkout dfcb7b6799 +RUN git apply ../patches/breakpad.diff RUN git clone https://chromium.googlesource.com/linux-syscall-support.git src/third_party/lss WORKDIR src/third_party/lss -RUN git checkout 8048ece +RUN git checkout e1e7b0ad8e + WORKDIR ${LibrariesPath} ENV BreakpadCache ${LibrariesPath}/breakpad-cache -RUN git clone https://chromium.googlesource.com/external/gyp.git -WORKDIR gyp -RUN git checkout 9f2a7bb1 -RUN git apply ../patches/gyp.diff - -WORKDIR ../breakpad +WORKDIR breakpad RUN ./configure RUN make -j$(nproc) RUN make DESTDIR="$BreakpadCache" install -WORKDIR src -RUN rm -rf testing -RUN git clone --depth=1 $GIT/google/googletest.git testing - -WORKDIR tools -RUN sed -i 's/minidump_upload.m/minidump_upload.cc/' linux/tools_linux.gypi -RUN ../../../gyp/gyp --depth=. --generator-output=.. -Goutput_dir=../out tools.gyp --format=cmake - -WORKDIR ../../out/Default -RUN cmake . -RUN cmake --build . --target dump_syms -j$(nproc) -RUN mv dump_syms $BreakpadCache - WORKDIR .. RUN rm -rf gyp diff --git a/Telegram/lib_ui b/Telegram/lib_ui index abfd57885..bd7c085bd 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit abfd5788516b5e96075c7b7982112d904be43ab6 +Subproject commit bd7c085bd019ec4a012154c3d290769672932a4e diff --git a/cmake b/cmake index 2034686f0..344074817 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 2034686f01d2207bde5b060e71a416f2f4419287 +Subproject commit 34407481713f987ea8b74bb949fd3d79e123e3c7 From 5d0445dd2531f1d0c30da6d7e676b7df2de4ecd5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 11:03:03 +0400 Subject: [PATCH 005/180] Remove unneeded namespace qualifiers. --- Telegram/SourceFiles/api/api_sending.cpp | 14 +++++++------- Telegram/SourceFiles/api/api_sending.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 795750ad2..2da7235b9 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -37,7 +37,7 @@ namespace Api { namespace { void InnerFillMessagePostFlags( - const Api::SendOptions &options, + const SendOptions &options, not_null peer, MessageFlags &flags) { const auto anonymousPost = peer->amAnonymous(); @@ -60,7 +60,7 @@ void InnerFillMessagePostFlags( template void SendExistingMedia( - Api::MessageToSend &&message, + MessageToSend &&message, not_null media, Fn inputMedia, Data::FileOrigin origin) { @@ -173,7 +173,7 @@ void SendExistingMedia( } // namespace void SendExistingDocument( - Api::MessageToSend &&message, + MessageToSend &&message, not_null document) { const auto inputMedia = [=] { return MTP_inputMediaDocument( @@ -194,7 +194,7 @@ void SendExistingDocument( } void SendExistingPhoto( - Api::MessageToSend &&message, + MessageToSend &&message, not_null photo) { const auto inputMedia = [=] { return MTP_inputMediaPhoto( @@ -209,7 +209,7 @@ void SendExistingPhoto( Data::FileOrigin()); } -bool SendDice(Api::MessageToSend &message) { +bool SendDice(MessageToSend &message) { const auto full = QStringView(message.textWithTags.text).trimmed(); auto length = 0; if (!Ui::Emoji::Find(full.data(), full.data() + full.size(), &length) @@ -313,7 +313,7 @@ bool SendDice(Api::MessageToSend &message) { } void FillMessagePostFlags( - const Api::SendAction &action, + const SendAction &action, not_null peer, MessageFlags &flags) { InnerFillMessagePostFlags(action.options, peer, flags); @@ -350,7 +350,7 @@ void SendConfirmedFile( const auto history = session->data().history(file->to.peer); const auto peer = history->peer; - auto action = Api::SendAction(history); + auto action = SendAction(history); action.options = file->to.options; action.clearDraft = false; action.replyTo = file->to.replyTo; diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h index fc07ecb45..8fe38b4e6 100644 --- a/Telegram/SourceFiles/api/api_sending.h +++ b/Telegram/SourceFiles/api/api_sending.h @@ -22,14 +22,14 @@ struct MessageToSend; struct SendAction; void SendExistingDocument( - Api::MessageToSend &&message, + MessageToSend &&message, not_null document); void SendExistingPhoto( - Api::MessageToSend &&message, + MessageToSend &&message, not_null photo); -bool SendDice(Api::MessageToSend &message); +bool SendDice(MessageToSend &message); void FillMessagePostFlags( const SendAction &action, From 3b2f6b893dec9fac6f664a11f855a52b19537662 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 16 Nov 2021 16:38:31 +0400 Subject: [PATCH 006/180] Hide MTPInputFile in Api::RemoteFileInfo. --- Telegram/SourceFiles/api/api_common.h | 6 +++ Telegram/SourceFiles/api/api_editing.cpp | 27 ++++------ Telegram/SourceFiles/api/api_editing.h | 12 ++--- Telegram/SourceFiles/api/api_media.cpp | 27 +++++----- Telegram/SourceFiles/api/api_media.h | 10 ++-- Telegram/SourceFiles/api/api_peer_photo.cpp | 4 +- Telegram/SourceFiles/apiwrap.cpp | 19 +++---- Telegram/SourceFiles/apiwrap.h | 11 ++-- Telegram/SourceFiles/storage/file_upload.cpp | 54 +++++++++---------- Telegram/SourceFiles/storage/file_upload.h | 22 +++----- .../window/themes/window_theme.cpp | 4 +- .../window/themes/window_theme_editor_box.cpp | 14 ++--- 12 files changed, 93 insertions(+), 117 deletions(-) diff --git a/Telegram/SourceFiles/api/api_common.h b/Telegram/SourceFiles/api/api_common.h index 63ee24691..112727cc5 100644 --- a/Telegram/SourceFiles/api/api_common.h +++ b/Telegram/SourceFiles/api/api_common.h @@ -45,4 +45,10 @@ struct MessageToSend { WebPageId webPageId = 0; }; +struct RemoteFileInfo { + MTPInputFile file; + std::optional thumb; + std::vector attachedStickers; +}; + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp index 2f083f389..46a120c54 100644 --- a/Telegram/SourceFiles/api/api_editing.cpp +++ b/Telegram/SourceFiles/api/api_editing.cpp @@ -172,33 +172,28 @@ void RescheduleMessage( void EditMessageWithUploadedDocument( HistoryItem *item, - const MTPInputFile &file, - const std::optional &thumb, - SendOptions options, - std::vector attachedStickers) { + RemoteFileInfo info, + SendOptions options) { if (!item || !item->media() || !item->media()->document()) { return; } - const auto media = PrepareUploadedDocument( + EditMessageWithUploadedMedia( item, - file, - thumb, - std::move(attachedStickers)); - EditMessageWithUploadedMedia(item, options, media); + options, + PrepareUploadedDocument(item, std::move(info))); } void EditMessageWithUploadedPhoto( HistoryItem *item, - const MTPInputFile &file, - SendOptions options, - std::vector attachedStickers) { + RemoteFileInfo info, + SendOptions options) { if (!item || !item->media() || !item->media()->photo()) { return; } - const auto media = PrepareUploadedPhoto( - file, - std::move(attachedStickers)); - EditMessageWithUploadedMedia(item, options, media); + EditMessageWithUploadedMedia( + item, + options, + PrepareUploadedPhoto(std::move(info))); } mtpRequestId EditCaption( diff --git a/Telegram/SourceFiles/api/api_editing.h b/Telegram/SourceFiles/api/api_editing.h index 87200cde2..4a80e9dae 100644 --- a/Telegram/SourceFiles/api/api_editing.h +++ b/Telegram/SourceFiles/api/api_editing.h @@ -16,6 +16,7 @@ class Error; namespace Api { struct SendOptions; +struct RemoteFileInfo; const auto kDefaultEditMessagesErrors = { u"MESSAGE_ID_INVALID"_q, @@ -29,16 +30,13 @@ void RescheduleMessage( void EditMessageWithUploadedDocument( HistoryItem *item, - const MTPInputFile &file, - const std::optional &thumb, - SendOptions options, - std::vector attachedStickers); + RemoteFileInfo info, + SendOptions options); void EditMessageWithUploadedPhoto( HistoryItem *item, - const MTPInputFile &file, - SendOptions options, - std::vector attachedStickers); + RemoteFileInfo info, + SendOptions options); mtpRequestId EditCaption( not_null item, diff --git a/Telegram/SourceFiles/api/api_media.cpp b/Telegram/SourceFiles/api/api_media.cpp index 517389d13..6038060f7 100644 --- a/Telegram/SourceFiles/api/api_media.cpp +++ b/Telegram/SourceFiles/api/api_media.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "api/api_media.h" +#include "api/api_common.h" #include "data/data_document.h" #include "data/stickers/data_stickers_set.h" #include "history/history_item.h" @@ -74,41 +75,39 @@ MTPVector ComposeSendingDocumentAttributes( } // namespace -MTPInputMedia PrepareUploadedPhoto( - const MTPInputFile &file, - std::vector attachedStickers) { - const auto flags = attachedStickers.empty() +MTPInputMedia PrepareUploadedPhoto(RemoteFileInfo info) { + const auto flags = info.attachedStickers.empty() ? MTPDinputMediaUploadedPhoto::Flags(0) : MTPDinputMediaUploadedPhoto::Flag::f_stickers; return MTP_inputMediaUploadedPhoto( MTP_flags(flags), - file, - MTP_vector(ranges::to(attachedStickers)), + info.file, + MTP_vector( + ranges::to(info.attachedStickers)), MTP_int(0)); } MTPInputMedia PrepareUploadedDocument( not_null item, - const MTPInputFile &file, - const std::optional &thumb, - std::vector attachedStickers) { + RemoteFileInfo info) { if (!item || !item->media() || !item->media()->document()) { return MTP_inputMediaEmpty(); } const auto emptyFlag = MTPDinputMediaUploadedDocument::Flags(0); using DocFlags = MTPDinputMediaUploadedDocument::Flag; const auto flags = emptyFlag - | (thumb ? DocFlags::f_thumb : emptyFlag) + | (info.thumb ? DocFlags::f_thumb : emptyFlag) | (item->groupId() ? DocFlags::f_nosound_video : emptyFlag) - | (attachedStickers.empty() ? DocFlags::f_stickers : emptyFlag); + | (info.attachedStickers.empty() ? DocFlags::f_stickers : emptyFlag); const auto document = item->media()->document(); return MTP_inputMediaUploadedDocument( MTP_flags(flags), - file, - thumb.value_or(MTPInputFile()), + info.file, + info.thumb.value_or(MTPInputFile()), MTP_string(document->mimeString()), ComposeSendingDocumentAttributes(document), - MTP_vector(ranges::to(attachedStickers)), + MTP_vector( + ranges::to(info.attachedStickers)), MTP_int(0)); } diff --git a/Telegram/SourceFiles/api/api_media.h b/Telegram/SourceFiles/api/api_media.h index d9dbaefb2..b9f744856 100644 --- a/Telegram/SourceFiles/api/api_media.h +++ b/Telegram/SourceFiles/api/api_media.h @@ -11,15 +11,13 @@ class HistoryItem; namespace Api { -MTPInputMedia PrepareUploadedPhoto( - const MTPInputFile &file, - std::vector attachedStickers); +struct RemoteFileInfo; + +MTPInputMedia PrepareUploadedPhoto(RemoteFileInfo info); MTPInputMedia PrepareUploadedDocument( not_null item, - const MTPInputFile &file, - const std::optional &thumb, - std::vector attachedStickers); + RemoteFileInfo info); bool HasAttachedStickers(MTPInputMedia media); diff --git a/Telegram/SourceFiles/api/api_peer_photo.cpp b/Telegram/SourceFiles/api/api_peer_photo.cpp index 1d75b38dc..28f47583f 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.cpp +++ b/Telegram/SourceFiles/api/api_peer_photo.cpp @@ -104,8 +104,8 @@ PeerPhoto::PeerPhoto(not_null api) // You can't use _session->lifetime() in the constructor, // only queued, because it is not constructed yet. _session->uploader().photoReady( - ) | rpl::start_with_next([=](const Storage::UploadedPhoto &data) { - ready(data.fullId, data.file); + ) | rpl::start_with_next([=](const Storage::UploadedMedia &data) { + ready(data.fullId, data.info.file); }, _session->lifetime()); }); } diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 011ae8a65..49d5eb682 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3963,13 +3963,10 @@ void ApiWrap::sendFile( void ApiWrap::sendUploadedPhoto( FullMsgId localId, - const MTPInputFile &file, - Api::SendOptions options, - std::vector attachedStickers) { + Api::RemoteFileInfo info, + Api::SendOptions options) { if (const auto item = _session->data().message(localId)) { - const auto media = Api::PrepareUploadedPhoto( - file, - std::move(attachedStickers)); + const auto media = Api::PrepareUploadedPhoto(std::move(info)); if (const auto groupId = item->groupId()) { uploadAlbumMedia(item, groupId, media); } else { @@ -3980,19 +3977,15 @@ void ApiWrap::sendUploadedPhoto( void ApiWrap::sendUploadedDocument( FullMsgId localId, - const MTPInputFile &file, - const std::optional &thumb, - Api::SendOptions options, - std::vector attachedStickers) { + Api::RemoteFileInfo info, + Api::SendOptions options) { if (const auto item = _session->data().message(localId)) { if (!item->media() || !item->media()->document()) { return; } const auto media = Api::PrepareUploadedDocument( item, - file, - thumb, - std::move(attachedStickers)); + std::move(info)); const auto groupId = item->groupId(); if (groupId) { uploadAlbumMedia(item, groupId, media); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 461d1327b..6fced4cc2 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -357,15 +357,12 @@ public: void sendUploadedPhoto( FullMsgId localId, - const MTPInputFile &file, - Api::SendOptions options, - std::vector attachedStickers); + Api::RemoteFileInfo info, + Api::SendOptions options); void sendUploadedDocument( FullMsgId localId, - const MTPInputFile &file, - const std::optional &thumb, - Api::SendOptions options, - std::vector attachedStickers); + Api::RemoteFileInfo file, + Api::SendOptions options); void cancelLocalItem(not_null item); diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index c06146225..664c8e0cb 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -158,40 +158,34 @@ Uploader::Uploader(not_null api) , _stopSessionsTimer([=] { stopSessions(); }) { const auto session = &_api->session(); photoReady( - ) | rpl::start_with_next([=](const UploadedPhoto &data) { + ) | rpl::start_with_next([=](UploadedMedia &&data) { if (data.edit) { const auto item = session->data().message(data.fullId); Api::EditMessageWithUploadedPhoto( item, - data.file, - data.options, - data.attachedStickers); + std::move(data.info), + data.options); } else { _api->sendUploadedPhoto( data.fullId, - data.file, - data.options, - data.attachedStickers); + std::move(data.info), + data.options); } }, _lifetime); documentReady( - ) | rpl::start_with_next([=](const UploadedDocument &data) { + ) | rpl::start_with_next([=](UploadedMedia &&data) { if (data.edit) { const auto item = session->data().message(data.fullId); Api::EditMessageWithUploadedDocument( item, - data.file, - data.thumb, - data.options, - data.attachedStickers); + std::move(data.info), + data.options); } else { _api->sendUploadedDocument( data.fullId, - data.file, - data.thumb, - data.options, - data.attachedStickers); + std::move(data.info), + data.options); } }, _lifetime); @@ -472,11 +466,14 @@ void Uploader::sendNext() { MTP_string(photoFilename), MTP_bytes(md5)); _photoReady.fire({ - uploadingId, - options, - file, - edit, - attachedStickers }); + .fullId = uploadingId, + .info = { + .file = file, + .attachedStickers = attachedStickers, + }, + .options = options, + .edit = edit, + }); } else if (uploadingData.type() == SendMediaType::File || uploadingData.type() == SendMediaType::ThemeFile || uploadingData.type() == SendMediaType::Audio) { @@ -510,12 +507,15 @@ void Uploader::sendNext() { MTP_bytes(thumbMd5)); }(); _documentReady.fire({ - uploadingId, - options, - file, - thumb, - edit, - attachedStickers }); + .fullId = uploadingId, + .info = { + .file = file, + .thumb = thumb, + .attachedStickers = attachedStickers, + }, + .options = options, + .edit = edit, + }); } else if (uploadingData.type() == SendMediaType::Secure) { _secureReady.fire({ uploadingId, diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 521adc05b..5bb893f92 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -28,21 +28,11 @@ namespace Storage { // MTP big files methods used for files greater than 10mb. constexpr auto kUseBigFilesFrom = 10 * 1024 * 1024; -struct UploadedPhoto { +struct UploadedMedia { FullMsgId fullId; + Api::RemoteFileInfo info; Api::SendOptions options; - MTPInputFile file; bool edit = false; - std::vector attachedStickers; -}; - -struct UploadedDocument { - FullMsgId fullId; - Api::SendOptions options; - MTPInputFile file; - std::optional thumb; - bool edit = false; - std::vector attachedStickers; }; struct UploadSecureProgress { @@ -75,10 +65,10 @@ public: void clear(); - rpl::producer photoReady() const { + rpl::producer photoReady() const { return _photoReady.events(); } - rpl::producer documentReady() const { + rpl::producer documentReady() const { return _documentReady.events(); } rpl::producer secureReady() const { @@ -138,8 +128,8 @@ private: std::map uploaded; base::Timer _nextTimer, _stopSessionsTimer; - rpl::event_stream _photoReady; - rpl::event_stream _documentReady; + rpl::event_stream _photoReady; + rpl::event_stream _documentReady; rpl::event_stream _secureReady; rpl::event_stream _photoProgress; rpl::event_stream _documentProgress; diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 3813ac809..24f9aca68 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -608,14 +608,14 @@ void ChatBackground::checkUploadWallPaper() { return; } _wallPaperUploadLifetime = _session->uploader().documentReady( - ) | rpl::start_with_next([=](const Storage::UploadedDocument &data) { + ) | rpl::start_with_next([=](const Storage::UploadedMedia &data) { if (data.fullId != _wallPaperUploadId) { return; } _wallPaperUploadId = FullMsgId(); _wallPaperRequestId = _session->api().request( MTPaccount_UploadWallPaper( - data.file, + data.info.file, MTP_string("image/jpeg"), _paper.mtpSettings() ) diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index b92defa50..3f736ba09 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -465,7 +465,7 @@ Fn SavePreparedTheme( Fn fail) { Expects(window->account().sessionExists()); - using Storage::UploadedDocument; + using Storage::UploadedMedia; struct State { FullMsgId id; bool generating = false; @@ -548,11 +548,11 @@ Fn SavePreparedTheme( }).send(); }; - const auto uploadTheme = [=](const UploadedDocument &data) { + const auto uploadTheme = [=](const UploadedMedia &data) { state->requestId = api->request(MTPaccount_UploadTheme( MTP_flags(MTPaccount_UploadTheme::Flag::f_thumb), - data.file, - *data.thumb, + data.info.file, + *data.info.thumb, MTP_string(state->filename), MTP_string("application/x-tgtheme-tdesktop") )).done([=](const MTPDocument &result) { @@ -575,9 +575,9 @@ Fn SavePreparedTheme( state->themeContent = theme; session->uploader().documentReady( - ) | rpl::filter([=](const UploadedDocument &data) { - return (data.fullId == state->id) && data.thumb.has_value(); - }) | rpl::start_with_next([=](const UploadedDocument &data) { + ) | rpl::filter([=](const UploadedMedia &data) { + return (data.fullId == state->id) && data.info.thumb.has_value(); + }) | rpl::start_with_next([=](const UploadedMedia &data) { uploadTheme(data); }, state->lifetime); From 53c15ed2acd890f783872ecb813d2a529b9baf5e Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 17 Nov 2021 20:00:00 +0400 Subject: [PATCH 007/180] Make SendingAlbum::Item::Item non-inline. --- Telegram/SourceFiles/storage/localimageloader.cpp | 4 ++++ Telegram/SourceFiles/storage/localimageloader.h | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index dc00d7af0..e54604d59 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -432,6 +432,10 @@ void SendingAlbum::removeItem(not_null item) { } } +SendingAlbum::Item::Item(TaskId taskId) +: taskId(taskId) { +} + FileLoadResult::FileLoadResult( TaskId taskId, uint64 id, diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index d0ebbc8ae..e2c44da02 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -168,8 +168,7 @@ private: struct SendingAlbum { struct Item { - explicit Item(TaskId taskId) : taskId(taskId) { - } + explicit Item(TaskId taskId); TaskId taskId; uint64 randomId = 0; From ebded1b4211041cf1e52fabf17c36d2d99a94772 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 11:40:15 +0400 Subject: [PATCH 008/180] Use Photo/DocumentData::isNull instead of date check. --- Telegram/SourceFiles/calls/calls_userpic.cpp | 2 +- Telegram/SourceFiles/data/data_document_resolver.cpp | 2 +- Telegram/SourceFiles/data/data_file_click_handler.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_userpic.cpp b/Telegram/SourceFiles/calls/calls_userpic.cpp index 446757af2..7b31a8c15 100644 --- a/Telegram/SourceFiles/calls/calls_userpic.cpp +++ b/Telegram/SourceFiles/calls/calls_userpic.cpp @@ -137,7 +137,7 @@ void Userpic::processPhoto() { _photo->wanted(Data::PhotoSize::Thumbnail, _peer->userpicPhotoOrigin()); } else { _photo = nullptr; - if (_peer->userpicPhotoUnknown() || (photo && !photo->date)) { + if (_peer->userpicPhotoUnknown() || (photo && photo->isNull())) { _peer->session().api().requestFullPeer(_peer); } } diff --git a/Telegram/SourceFiles/data/data_document_resolver.cpp b/Telegram/SourceFiles/data/data_document_resolver.cpp index ef2d51e7c..7c2335734 100644 --- a/Telegram/SourceFiles/data/data_document_resolver.cpp +++ b/Telegram/SourceFiles/data/data_document_resolver.cpp @@ -206,7 +206,7 @@ void ResolveDocument( Window::SessionController *controller, not_null document, HistoryItem *item) { - if (!document->date) { + if (document->isNull()) { return; } const auto msgId = item ? item->fullId() : FullMsgId(); diff --git a/Telegram/SourceFiles/data/data_file_click_handler.cpp b/Telegram/SourceFiles/data/data_file_click_handler.cpp index e4a9ad934..ae82e775f 100644 --- a/Telegram/SourceFiles/data/data_file_click_handler.cpp +++ b/Telegram/SourceFiles/data/data_file_click_handler.cpp @@ -63,7 +63,7 @@ void DocumentSaveClickHandler::Save( Data::FileOrigin origin, not_null data, Mode mode) { - if (!data->date) { + if (data->isNull()) { return; } @@ -107,7 +107,7 @@ DocumentCancelClickHandler::DocumentCancelClickHandler( void DocumentCancelClickHandler::onClickImpl() const { const auto data = document(); - if (!data->date) { + if (data->isNull()) { return; } else if (data->uploading() && _handler) { _handler(context()); @@ -119,7 +119,7 @@ void DocumentCancelClickHandler::onClickImpl() const { void DocumentOpenWithClickHandler::Open( Data::FileOrigin origin, not_null data) { - if (!data->date) { + if (data->isNull()) { return; } @@ -171,7 +171,7 @@ void PhotoOpenClickHandler::onClickImpl() const { void PhotoSaveClickHandler::onClickImpl() const { const auto data = photo(); - if (!data->date) { + if (data->isNull()) { return; } else { data->clearFailed(Data::PhotoSize::Large); @@ -189,7 +189,7 @@ PhotoCancelClickHandler::PhotoCancelClickHandler( void PhotoCancelClickHandler::onClickImpl() const { const auto data = photo(); - if (!data->date) { + if (data->isNull()) { return; } else if (data->uploading() && _handler) { _handler(context()); From f2e4a5a35a7522fe6d8345e8816c3d36336bb5a4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 16:03:12 +0400 Subject: [PATCH 009/180] Highlight YouTube video timestamps as external links. --- .../SourceFiles/core/click_handler_types.cpp | 20 ++++++ .../SourceFiles/core/click_handler_types.h | 8 +-- .../SourceFiles/core/local_url_handlers.cpp | 13 ++++ .../history/history_item_components.cpp | 10 ++- .../history/history_item_components.h | 4 +- .../SourceFiles/history/history_message.cpp | 51 +++++++++----- .../view/media/history_view_document.cpp | 7 +- .../history/view/media/history_view_gif.cpp | 6 +- .../history/view/media/history_view_media.cpp | 69 +++++++++++++++++-- .../history/view/media/history_view_media.h | 11 ++- .../view/media/history_view_media_grouped.cpp | 14 ++-- .../media/view/media_view_overlay_widget.cpp | 10 +-- 12 files changed, 170 insertions(+), 53 deletions(-) diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 74a9009e9..151b07085 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -65,6 +65,26 @@ bool UrlRequiresConfirmation(const QUrl &url) { RegExOption::CaseInsensitive); } +QString HiddenUrlClickHandler::copyToClipboardText() const { + return url().startsWith(qstr("internal:url:")) + ? url().mid(qstr("internal:url:").size()) + : url(); +} + +QString HiddenUrlClickHandler::copyToClipboardContextItemText() const { + return url().isEmpty() + ? QString() + : !url().startsWith(qstr("internal:")) + ? UrlClickHandler::copyToClipboardContextItemText() + : url().startsWith(qstr("internal:url:")) + ? UrlClickHandler::copyToClipboardContextItemText() + : QString(); +} + +QString HiddenUrlClickHandler::dragText() const { + return HiddenUrlClickHandler::copyToClipboardText(); +} + void HiddenUrlClickHandler::Open(QString url, QVariant context) { url = Core::TryConvertUrlToLocal(url); if (Core::InternalPassportLink(url)) { diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index a4ff0f8b1..b68d212e4 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -39,11 +39,9 @@ class HiddenUrlClickHandler : public UrlClickHandler { public: HiddenUrlClickHandler(QString url) : UrlClickHandler(url, false) { } - QString copyToClipboardContextItemText() const override { - return (url().isEmpty() || url().startsWith(qstr("internal:"))) - ? QString() - : UrlClickHandler::copyToClipboardContextItemText(); - } + QString copyToClipboardText() const override; + QString copyToClipboardContextItemText() const override; + QString dragText() const override; static void Open(QString url, QVariant context = {}); void onClick(ClickContext context) const override { diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index f4c7c5b8f..e45295f4e 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -474,6 +474,15 @@ bool ShowInviteLink( return true; } +bool OpenExternalLink( + Window::SessionController *controller, + const Match &match, + const QVariant &context) { + return Ui::Integration::Instance().handleUrlClick( + match->captured(1), + context); +} + void ExportTestChatTheme( not_null session, not_null theme) { @@ -698,6 +707,10 @@ const std::vector &InternalUrlHandlers() { qsl("^show_invite_link/?\\?link=([a-zA-Z0-9_\\+\\/\\=\\-]+)(&|$)"), ShowInviteLink }, + { + qsl("^url:(.+)$"), + OpenExternalLink + }, }; return Result; } diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 50e8818c5..9f1327872 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_file_origin.h" #include "data/data_document.h" +#include "data/data_web_page.h" #include "data/data_file_click_handler.h" #include "main/main_session.h" #include "window/window_session_controller.h" @@ -224,7 +225,7 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const { bool HistoryMessageReply::updateData( not_null holder, bool force) { - const auto guard = gsl::finally([&] { refreshReplyToDocument(); }); + const auto guard = gsl::finally([&] { refreshReplyToMedia(); }); if (!force) { if (replyToMsg || !replyToMsgId) { return true; @@ -291,7 +292,7 @@ void HistoryMessageReply::clearData(not_null holder) { replyToMsg = nullptr; } replyToMsgId = 0; - refreshReplyToDocument(); + refreshReplyToMedia(); } bool HistoryMessageReply::isNameUpdated() const { @@ -416,11 +417,14 @@ void HistoryMessageReply::paint( } } -void HistoryMessageReply::refreshReplyToDocument() { +void HistoryMessageReply::refreshReplyToMedia() { replyToDocumentId = 0; + replyToWebPageId = 0; if (const auto media = replyToMsg ? replyToMsg->media() : nullptr) { if (const auto document = media->document()) { replyToDocumentId = document->id; + } else if (const auto webpage = media->webpage()) { + replyToWebPageId = webpage->id; } } } diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h index b41504da2..880b10639 100644 --- a/Telegram/SourceFiles/history/history_item_components.h +++ b/Telegram/SourceFiles/history/history_item_components.h @@ -130,6 +130,7 @@ struct HistoryMessageReply : public RuntimeComponent holder); - void refreshReplyToDocument(); + void refreshReplyToMedia(); PeerId replyToPeerId = 0; MsgId replyToMsgId = 0; MsgId replyToMsgTop = 0; HistoryItem *replyToMsg = nullptr; DocumentId replyToDocumentId = 0; + WebPageId replyToWebPageId = 0; ClickHandlerPtr replyToLnk; mutable Ui::Text::String replyToName, replyToText; mutable int replyToVersion = 0; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index f6c1542c4..7ef0b990f 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_user.h" #include "data/data_histories.h" +#include "data/data_web_page.h" #include "styles/style_dialogs.h" #include "styles/style_widgets.h" #include "styles/style_chat.h" @@ -1000,9 +1001,11 @@ void HistoryMessage::setCommentsItemId(FullMsgId id) { bool HistoryMessage::updateDependencyItem() { if (const auto reply = Get()) { const auto documentId = reply->replyToDocumentId; + const auto webpageId = reply->replyToWebPageId; const auto result = reply->updateData(this, true); - if (documentId != reply->replyToDocumentId - && generateLocalEntitiesByReply()) { + const auto mediaIdChanged = (documentId != reply->replyToDocumentId) + || (webpageId != reply->replyToWebPageId); + if (mediaIdChanged && generateLocalEntitiesByReply()) { reapplyText(); } return result; @@ -1524,34 +1527,50 @@ Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const { } bool HistoryMessage::generateLocalEntitiesByReply() const { - return !_media || _media->webpage(); + if (!_media) { + return true; + } else if (const auto webpage = _media->webpage()) { + return !webpage->document && webpage->type != WebPageType::Video; + } + return false; } TextWithEntities HistoryMessage::withLocalEntities( const TextWithEntities &textWithEntities) const { + using namespace HistoryView; if (!generateLocalEntitiesByReply()) { + if (const auto webpage = _media ? _media->webpage() : nullptr) { + if (const auto duration = DurationForTimestampLinks(webpage)) { + return AddTimestampLinks( + textWithEntities, + duration, + TimestampLinkBase(webpage, fullId())); + } + } return textWithEntities; } if (const auto reply = Get()) { const auto document = reply->replyToDocumentId ? history()->owner().document(reply->replyToDocumentId).get() : nullptr; - if (document - && (document->isVideoFile() - || document->isSong() - || document->isVoiceMessage())) { - using namespace HistoryView; - const auto duration = document->getDuration(); - const auto base = (duration > 0) - ? DocumentTimestampLinkBase( - document, - reply->replyToMsg->fullId()) - : QString(); - if (!base.isEmpty()) { + const auto webpage = reply->replyToWebPageId + ? history()->owner().webpage(reply->replyToWebPageId).get() + : nullptr; + if (document) { + if (const auto duration = DurationForTimestampLinks(document)) { + const auto context = reply->replyToMsg->fullId(); return AddTimestampLinks( textWithEntities, duration, - base); + TimestampLinkBase(document, context)); + } + } else if (webpage) { + if (const auto duration = DurationForTimestampLinks(webpage)) { + const auto context = reply->replyToMsg->fullId(); + return AddTimestampLinks( + textWithEntities, + duration, + TimestampLinkBase(webpage, context)); } } } diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index c5244fd01..502e7b487 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -1084,12 +1084,9 @@ TextWithEntities Document::getCaption() const { } Ui::Text::String Document::createCaption() { - const auto timestampLinksDuration = (_data->isSong() - || _data->isVoiceMessage()) - ? _data->getDuration() - : 0; + const auto timestampLinksDuration = DurationForTimestampLinks(_data); const auto timestampLinkBase = timestampLinksDuration - ? DocumentTimestampLinkBase(_data, _realParent->fullId()) + ? TimestampLinkBase(_data, _realParent->fullId()) : QString(); return File::createCaption( _realParent, diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 1e025b7fd..75772a124 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -1322,11 +1322,9 @@ void Gif::refreshParentId(not_null realParent) { } void Gif::refreshCaption() { - const auto timestampLinksDuration = _data->isVideoFile() - ? _data->getDuration() - : 0; + const auto timestampLinksDuration = DurationForTimestampLinks(_data); const auto timestampLinkBase = timestampLinksDuration - ? DocumentTimestampLinkBase(_data, _realParent->fullId()) + ? TimestampLinkBase(_data, _realParent->fullId()) : QString(); _caption = createCaption( _parent->data(), diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index 09d24d454..5e3cebf77 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_single_player.h" #include "storage/storage_shared_media.h" #include "data/data_document.h" +#include "data/data_web_page.h" #include "ui/item_text_options.h" #include "ui/chat/chat_style.h" #include "ui/chat/message_bubble.h" @@ -44,18 +45,77 @@ namespace { } // namespace -QString DocumentTimestampLinkBase( +TimeId DurationForTimestampLinks(not_null document) { + if (!document->isVideoFile() + && !document->isSong() + && !document->isVoiceMessage()) { + return TimeId(0); + } + return std::max(document->getDuration(), TimeId(0)); +} + +QString TimestampLinkBase( not_null document, FullMsgId context) { return QString( - "doc%1_%2_%3" + "media_timestamp?base=doc%1_%2_%3&t=" ).arg(document->id).arg(context.channel.bare).arg(context.msg.bare); } +TimeId DurationForTimestampLinks(not_null webpage) { + if (!webpage->collage.items.empty()) { + return false; + } else if (const auto document = webpage->document) { + return DurationForTimestampLinks(document); + } else if (webpage->type != WebPageType::Video + || webpage->siteName != qstr("YouTube")) { + return TimeId(0); + } else if (webpage->duration > 0) { + return webpage->duration; + } + constexpr auto kMaxYouTubeTimestampDuration = 10 * 60 * TimeId(60); + return kMaxYouTubeTimestampDuration; +} + +QString TimestampLinkBase( + not_null webpage, + FullMsgId context) { + const auto url = webpage->url; + if (url.isEmpty()) { + return QString(); + } + auto parts = url.split(QChar('#')); + const auto base = parts[0]; + parts.pop_front(); + const auto use = [&] { + const auto query = base.indexOf(QChar('?')); + if (query < 0) { + return base + QChar('?'); + } + auto params = base.mid(query + 1).split(QChar('&')); + for (auto i = params.begin(); i != params.end();) { + if (i->startsWith("t=")) { + i = params.erase(i); + } else { + ++i; + } + } + return base.mid(0, query) + + (params.empty() ? "?" : ("?" + params.join(QChar('&')) + "&")); + }(); + return "url:" + + use + + "t=" + + (parts.empty() ? QString() : ("#" + parts.join(QChar('#')))); +} + TextWithEntities AddTimestampLinks( TextWithEntities text, TimeId duration, const QString &base) { + if (base.isEmpty()) { + return text; + } static const auto expression = QRegularExpression( "(? document); +[[nodiscard]] QString TimestampLinkBase( not_null document, FullMsgId context); + +[[nodiscard]] TimeId DurationForTimestampLinks( + not_null webpage); +[[nodiscard]] QString TimestampLinkBase( + not_null webpage, + FullMsgId context); + [[nodiscard]] TextWithEntities AddTimestampLinks( TextWithEntities text, TimeId duration, diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index a2992af1a..aa26f92f2 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -677,18 +677,16 @@ void GroupedMedia::updateNeedBubbleState() { QString base; }; const auto timestamp = [&]() -> Timestamp { - const auto &document = part->content->getDocument(); - if (!document || document->isAnimation()) { + const auto document = part->content->getDocument(); + const auto duration = document + ? DurationForTimestampLinks(document) + : TimeId(0); + if (!duration) { return {}; } - const auto duration = document->getDuration(); return { .duration = duration, - .base = duration - ? DocumentTimestampLinkBase( - document, - part->item->fullId()) - : QString(), + .base = TimestampLinkBase(document, part->item->fullId()), }; }(); _caption = createCaption( diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index d3d1d9900..6618b4cc8 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2102,18 +2102,20 @@ void OverlayWidget::refreshCaption() { using namespace HistoryView; _caption = Ui::Text::String(st::msgMinWidth); - const auto duration = (_streamed && _document && !videoIsGifOrUserpic()) - ? _document->getDuration() + const auto duration = (_streamed && _document) + ? DurationForTimestampLinks(_document) : 0; const auto base = duration - ? DocumentTimestampLinkBase(_document, _message->fullId()) + ? TimestampLinkBase(_document, _message->fullId()) : QString(); const auto context = Core::MarkedTextContext{ .session = &_message->history()->session() }; _caption.setMarkedText( st::mediaviewCaptionStyle, - AddTimestampLinks(caption, duration, base), + (base.isEmpty() + ? caption + : AddTimestampLinks(caption, duration, base)), Ui::ItemTextOptions(_message), context); } From 33f56a791a614c45b44288d09a02bc5448128baa Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 17:13:04 +0400 Subject: [PATCH 010/180] Show correct tooltip on YouTube timestamps. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index bd7c085bd..1111718ea 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit bd7c085bd019ec4a012154c3d290769672932a4e +Subproject commit 1111718ea3d78c1abeb3d94c11b36e540bc94c4a From dc8aefd7adb44a3bfddf8f9552ac02c4571cd974 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 18 Nov 2021 19:13:40 +0400 Subject: [PATCH 011/180] Use GCC 10 in snap --- snap/snapcraft.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 49babc948..6c886298f 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -91,9 +91,12 @@ parts: source-type: git parse-info: [usr/share/metainfo/telegram-desktop_telegram-desktop.appdata.xml] build-environment: + - CC: gcc-10 + - CXX: g++-10 - LD_LIBRARY_PATH: $SNAPCRAFT_STAGE/usr/lib/$SNAPCRAFT_ARCH_TRIPLET:$LD_LIBRARY_PATH build-packages: - clang + - g++-10 - python - libasound2-dev - libglib2.0-dev From 55e44e36750dac1f8c3451bc9b5a37b1c2a74609 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 18 Nov 2021 19:13:59 +0400 Subject: [PATCH 012/180] Use GCC 10 in GH action --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index b4ec6f93d..3b9f053a9 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -53,7 +53,7 @@ jobs: defaults: run: - shell: scl enable llvm-toolset-7.0 -- scl enable devtoolset-9 -- bash --noprofile --norc -eo pipefail {0} + shell: scl enable llvm-toolset-7.0 -- scl enable devtoolset-10 -- bash --noprofile --norc -eo pipefail {0} strategy: matrix: From a55dbe212aca36b208eb6794bc76e004fb9acebe Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 18 Nov 2021 19:14:57 +0400 Subject: [PATCH 013/180] Cleanup unneeded commands in Dockerfile --- Telegram/build/docker/centos_env/Dockerfile | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 953b827a6..848406bbd 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -808,17 +808,12 @@ RUN git clone https://chromium.googlesource.com/linux-syscall-support.git src/th WORKDIR src/third_party/lss RUN git checkout e1e7b0ad8e -WORKDIR ${LibrariesPath} - -ENV BreakpadCache ${LibrariesPath}/breakpad-cache - -WORKDIR breakpad +WORKDIR ../../.. RUN ./configure RUN make -j$(nproc) -RUN make DESTDIR="$BreakpadCache" install +RUN make DESTDIR="${LibrariesPath}/breakpad-cache" install WORKDIR .. -RUN rm -rf gyp FROM builder AS webrtc From 6b24eb6d8e3e659c9e0f5581f6f3113b0aec33ab Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 18 Nov 2021 20:09:28 +0400 Subject: [PATCH 014/180] Update lib_webview --- Telegram/lib_webview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_webview b/Telegram/lib_webview index d533a9c40..f82ad9d60 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit d533a9c404c45b350bd056ee86f4efab72f02ff7 +Subproject commit f82ad9d608000171ef3822713712346f38aee50d From f4a6be2ed9223e1c27f90bfd517893dbc6e89349 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 17:13:44 +0400 Subject: [PATCH 015/180] Move audio player icons to "player" folder. --- .../icons/{ => player}/player_check.png | Bin .../icons/{ => player}/player_check@2x.png | Bin .../icons/{ => player}/player_check@3x.png | Bin .../icons/{ => player}/player_close.png | Bin .../icons/{ => player}/player_close@2x.png | Bin .../icons/{ => player}/player_close@3x.png | Bin .../icons/{ => player}/player_fullscreen.png | Bin .../{ => player}/player_fullscreen@2x.png | Bin .../{ => player}/player_fullscreen@3x.png | Bin .../icons/{ => player}/player_minimize.png | Bin .../icons/{ => player}/player_minimize@2x.png | Bin .../icons/{ => player}/player_minimize@3x.png | Bin .../icons/{ => player}/player_more.png | Bin .../icons/{ => player}/player_more@2x.png | Bin .../icons/{ => player}/player_more@3x.png | Bin .../icons/{ => player}/player_next.png | Bin .../icons/{ => player}/player_next@2x.png | Bin .../icons/{ => player}/player_next@3x.png | Bin .../icons/{ => player}/player_panel_next.png | Bin .../{ => player}/player_panel_next@2x.png | Bin .../{ => player}/player_panel_next@3x.png | Bin .../icons/{ => player}/player_panel_pin.png | Bin .../{ => player}/player_panel_pin@2x.png | Bin .../{ => player}/player_panel_pin@3x.png | Bin .../icons/{ => player}/player_pause.png | Bin .../icons/{ => player}/player_pause@2x.png | Bin .../icons/{ => player}/player_pause@3x.png | Bin .../icons/{ => player}/player_pip.png | Bin .../icons/{ => player}/player_pip@2x.png | Bin .../icons/{ => player}/player_pip@3x.png | Bin .../icons/{ => player}/player_pip_close.png | Bin .../{ => player}/player_pip_close@2x.png | Bin .../{ => player}/player_pip_close@3x.png | Bin .../icons/{ => player}/player_pip_enlarge.png | Bin .../{ => player}/player_pip_enlarge@2x.png | Bin .../{ => player}/player_pip_enlarge@3x.png | Bin .../icons/{ => player}/player_pip_pause.png | Bin .../{ => player}/player_pip_pause@2x.png | Bin .../{ => player}/player_pip_pause@3x.png | Bin .../icons/{ => player}/player_pip_play.png | Bin .../icons/{ => player}/player_pip_play@2x.png | Bin .../icons/{ => player}/player_pip_play@3x.png | Bin .../icons/{ => player}/player_play.png | Bin .../icons/{ => player}/player_play@2x.png | Bin .../icons/{ => player}/player_play@3x.png | Bin .../icons/{ => player}/player_repeat.png | Bin .../icons/{ => player}/player_repeat@2x.png | Bin .../icons/{ => player}/player_repeat@3x.png | Bin .../icons/{ => player}/player_volume0.png | Bin .../icons/{ => player}/player_volume0@2x.png | Bin .../icons/{ => player}/player_volume0@3x.png | Bin .../icons/{ => player}/player_volume1.png | Bin .../icons/{ => player}/player_volume1@2x.png | Bin .../icons/{ => player}/player_volume1@3x.png | Bin .../icons/{ => player}/player_volume2.png | Bin .../icons/{ => player}/player_volume2@2x.png | Bin .../icons/{ => player}/player_volume2@3x.png | Bin .../icons/{ => player}/player_volume3.png | Bin .../icons/{ => player}/player_volume3@2x.png | Bin .../icons/{ => player}/player_volume3@3x.png | Bin .../icons/{ => player}/player_volume_off.png | Bin .../{ => player}/player_volume_off@2x.png | Bin .../{ => player}/player_volume_off@3x.png | Bin .../icons/{ => player}/player_volume_on.png | Bin .../{ => player}/player_volume_on@2x.png | Bin .../{ => player}/player_volume_on@3x.png | Bin .../{ => player}/player_volume_small.png | Bin .../{ => player}/player_volume_small@2x.png | Bin .../{ => player}/player_volume_small@3x.png | Bin .../icons/{ => player}/playlist_cancel.png | Bin .../icons/{ => player}/playlist_cancel@2x.png | Bin .../icons/{ => player}/playlist_cancel@3x.png | Bin .../icons/{ => player}/playlist_download.png | Bin .../{ => player}/playlist_download@2x.png | Bin .../{ => player}/playlist_download@3x.png | Bin .../icons/{ => player}/playlist_pause.png | Bin .../icons/{ => player}/playlist_pause@2x.png | Bin .../icons/{ => player}/playlist_pause@3x.png | Bin .../icons/{ => player}/playlist_play.png | Bin .../icons/{ => player}/playlist_play@2x.png | Bin .../icons/{ => player}/playlist_play@3x.png | Bin .../icons/{ => player}/playlist_shadow.png | Bin .../icons/{ => player}/playlist_shadow@2x.png | Bin .../icons/{ => player}/playlist_shadow@3x.png | Bin .../voice_speed/voice_speed0.5.png | Bin .../voice_speed/voice_speed0.5@2x.png | Bin .../voice_speed/voice_speed0.5@3x.png | Bin .../voice_speed/voice_speed1.5.png | Bin .../voice_speed/voice_speed1.5@2x.png | Bin .../voice_speed/voice_speed1.5@3x.png | Bin .../{ => player}/voice_speed/voice_speed2.png | Bin .../voice_speed/voice_speed2@2x.png | Bin .../voice_speed/voice_speed2@3x.png | Bin .../media/player/media_player.style | 74 ++++++++---------- .../SourceFiles/media/view/media_view.style | 66 ++++++++-------- Telegram/SourceFiles/overview/overview.style | 32 ++++---- Telegram/SourceFiles/ui/chat/chat.style | 8 +- 97 files changed, 87 insertions(+), 93 deletions(-) rename Telegram/Resources/icons/{ => player}/player_check.png (100%) rename Telegram/Resources/icons/{ => player}/player_check@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_check@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_close.png (100%) rename Telegram/Resources/icons/{ => player}/player_close@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_close@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_fullscreen.png (100%) rename Telegram/Resources/icons/{ => player}/player_fullscreen@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_fullscreen@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_minimize.png (100%) rename Telegram/Resources/icons/{ => player}/player_minimize@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_minimize@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_more.png (100%) rename Telegram/Resources/icons/{ => player}/player_more@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_more@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_next.png (100%) rename Telegram/Resources/icons/{ => player}/player_next@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_next@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_panel_next.png (100%) rename Telegram/Resources/icons/{ => player}/player_panel_next@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_panel_next@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_panel_pin.png (100%) rename Telegram/Resources/icons/{ => player}/player_panel_pin@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_panel_pin@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pause.png (100%) rename Telegram/Resources/icons/{ => player}/player_pause@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pause@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_close.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_close@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_close@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_enlarge.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_enlarge@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_enlarge@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_pause.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_pause@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_pause@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_play.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_play@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_pip_play@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_play.png (100%) rename Telegram/Resources/icons/{ => player}/player_play@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_play@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_repeat.png (100%) rename Telegram/Resources/icons/{ => player}/player_repeat@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_repeat@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume0.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume0@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume0@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume1.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume1@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume1@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume2.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume2@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume2@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume3.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume3@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume3@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_off.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_off@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_off@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_on.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_on@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_on@3x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_small.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_small@2x.png (100%) rename Telegram/Resources/icons/{ => player}/player_volume_small@3x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_cancel.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_cancel@2x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_cancel@3x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_download.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_download@2x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_download@3x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_pause.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_pause@2x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_pause@3x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_play.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_play@2x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_play@3x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_shadow.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_shadow@2x.png (100%) rename Telegram/Resources/icons/{ => player}/playlist_shadow@3x.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed0.5.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed0.5@2x.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed0.5@3x.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed1.5.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed1.5@2x.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed1.5@3x.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed2.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed2@2x.png (100%) rename Telegram/Resources/icons/{ => player}/voice_speed/voice_speed2@3x.png (100%) diff --git a/Telegram/Resources/icons/player_check.png b/Telegram/Resources/icons/player/player_check.png similarity index 100% rename from Telegram/Resources/icons/player_check.png rename to Telegram/Resources/icons/player/player_check.png diff --git a/Telegram/Resources/icons/player_check@2x.png b/Telegram/Resources/icons/player/player_check@2x.png similarity index 100% rename from Telegram/Resources/icons/player_check@2x.png rename to Telegram/Resources/icons/player/player_check@2x.png diff --git a/Telegram/Resources/icons/player_check@3x.png b/Telegram/Resources/icons/player/player_check@3x.png similarity index 100% rename from Telegram/Resources/icons/player_check@3x.png rename to Telegram/Resources/icons/player/player_check@3x.png diff --git a/Telegram/Resources/icons/player_close.png b/Telegram/Resources/icons/player/player_close.png similarity index 100% rename from Telegram/Resources/icons/player_close.png rename to Telegram/Resources/icons/player/player_close.png diff --git a/Telegram/Resources/icons/player_close@2x.png b/Telegram/Resources/icons/player/player_close@2x.png similarity index 100% rename from Telegram/Resources/icons/player_close@2x.png rename to Telegram/Resources/icons/player/player_close@2x.png diff --git a/Telegram/Resources/icons/player_close@3x.png b/Telegram/Resources/icons/player/player_close@3x.png similarity index 100% rename from Telegram/Resources/icons/player_close@3x.png rename to Telegram/Resources/icons/player/player_close@3x.png diff --git a/Telegram/Resources/icons/player_fullscreen.png b/Telegram/Resources/icons/player/player_fullscreen.png similarity index 100% rename from Telegram/Resources/icons/player_fullscreen.png rename to Telegram/Resources/icons/player/player_fullscreen.png diff --git a/Telegram/Resources/icons/player_fullscreen@2x.png b/Telegram/Resources/icons/player/player_fullscreen@2x.png similarity index 100% rename from Telegram/Resources/icons/player_fullscreen@2x.png rename to Telegram/Resources/icons/player/player_fullscreen@2x.png diff --git a/Telegram/Resources/icons/player_fullscreen@3x.png b/Telegram/Resources/icons/player/player_fullscreen@3x.png similarity index 100% rename from Telegram/Resources/icons/player_fullscreen@3x.png rename to Telegram/Resources/icons/player/player_fullscreen@3x.png diff --git a/Telegram/Resources/icons/player_minimize.png b/Telegram/Resources/icons/player/player_minimize.png similarity index 100% rename from Telegram/Resources/icons/player_minimize.png rename to Telegram/Resources/icons/player/player_minimize.png diff --git a/Telegram/Resources/icons/player_minimize@2x.png b/Telegram/Resources/icons/player/player_minimize@2x.png similarity index 100% rename from Telegram/Resources/icons/player_minimize@2x.png rename to Telegram/Resources/icons/player/player_minimize@2x.png diff --git a/Telegram/Resources/icons/player_minimize@3x.png b/Telegram/Resources/icons/player/player_minimize@3x.png similarity index 100% rename from Telegram/Resources/icons/player_minimize@3x.png rename to Telegram/Resources/icons/player/player_minimize@3x.png diff --git a/Telegram/Resources/icons/player_more.png b/Telegram/Resources/icons/player/player_more.png similarity index 100% rename from Telegram/Resources/icons/player_more.png rename to Telegram/Resources/icons/player/player_more.png diff --git a/Telegram/Resources/icons/player_more@2x.png b/Telegram/Resources/icons/player/player_more@2x.png similarity index 100% rename from Telegram/Resources/icons/player_more@2x.png rename to Telegram/Resources/icons/player/player_more@2x.png diff --git a/Telegram/Resources/icons/player_more@3x.png b/Telegram/Resources/icons/player/player_more@3x.png similarity index 100% rename from Telegram/Resources/icons/player_more@3x.png rename to Telegram/Resources/icons/player/player_more@3x.png diff --git a/Telegram/Resources/icons/player_next.png b/Telegram/Resources/icons/player/player_next.png similarity index 100% rename from Telegram/Resources/icons/player_next.png rename to Telegram/Resources/icons/player/player_next.png diff --git a/Telegram/Resources/icons/player_next@2x.png b/Telegram/Resources/icons/player/player_next@2x.png similarity index 100% rename from Telegram/Resources/icons/player_next@2x.png rename to Telegram/Resources/icons/player/player_next@2x.png diff --git a/Telegram/Resources/icons/player_next@3x.png b/Telegram/Resources/icons/player/player_next@3x.png similarity index 100% rename from Telegram/Resources/icons/player_next@3x.png rename to Telegram/Resources/icons/player/player_next@3x.png diff --git a/Telegram/Resources/icons/player_panel_next.png b/Telegram/Resources/icons/player/player_panel_next.png similarity index 100% rename from Telegram/Resources/icons/player_panel_next.png rename to Telegram/Resources/icons/player/player_panel_next.png diff --git a/Telegram/Resources/icons/player_panel_next@2x.png b/Telegram/Resources/icons/player/player_panel_next@2x.png similarity index 100% rename from Telegram/Resources/icons/player_panel_next@2x.png rename to Telegram/Resources/icons/player/player_panel_next@2x.png diff --git a/Telegram/Resources/icons/player_panel_next@3x.png b/Telegram/Resources/icons/player/player_panel_next@3x.png similarity index 100% rename from Telegram/Resources/icons/player_panel_next@3x.png rename to Telegram/Resources/icons/player/player_panel_next@3x.png diff --git a/Telegram/Resources/icons/player_panel_pin.png b/Telegram/Resources/icons/player/player_panel_pin.png similarity index 100% rename from Telegram/Resources/icons/player_panel_pin.png rename to Telegram/Resources/icons/player/player_panel_pin.png diff --git a/Telegram/Resources/icons/player_panel_pin@2x.png b/Telegram/Resources/icons/player/player_panel_pin@2x.png similarity index 100% rename from Telegram/Resources/icons/player_panel_pin@2x.png rename to Telegram/Resources/icons/player/player_panel_pin@2x.png diff --git a/Telegram/Resources/icons/player_panel_pin@3x.png b/Telegram/Resources/icons/player/player_panel_pin@3x.png similarity index 100% rename from Telegram/Resources/icons/player_panel_pin@3x.png rename to Telegram/Resources/icons/player/player_panel_pin@3x.png diff --git a/Telegram/Resources/icons/player_pause.png b/Telegram/Resources/icons/player/player_pause.png similarity index 100% rename from Telegram/Resources/icons/player_pause.png rename to Telegram/Resources/icons/player/player_pause.png diff --git a/Telegram/Resources/icons/player_pause@2x.png b/Telegram/Resources/icons/player/player_pause@2x.png similarity index 100% rename from Telegram/Resources/icons/player_pause@2x.png rename to Telegram/Resources/icons/player/player_pause@2x.png diff --git a/Telegram/Resources/icons/player_pause@3x.png b/Telegram/Resources/icons/player/player_pause@3x.png similarity index 100% rename from Telegram/Resources/icons/player_pause@3x.png rename to Telegram/Resources/icons/player/player_pause@3x.png diff --git a/Telegram/Resources/icons/player_pip.png b/Telegram/Resources/icons/player/player_pip.png similarity index 100% rename from Telegram/Resources/icons/player_pip.png rename to Telegram/Resources/icons/player/player_pip.png diff --git a/Telegram/Resources/icons/player_pip@2x.png b/Telegram/Resources/icons/player/player_pip@2x.png similarity index 100% rename from Telegram/Resources/icons/player_pip@2x.png rename to Telegram/Resources/icons/player/player_pip@2x.png diff --git a/Telegram/Resources/icons/player_pip@3x.png b/Telegram/Resources/icons/player/player_pip@3x.png similarity index 100% rename from Telegram/Resources/icons/player_pip@3x.png rename to Telegram/Resources/icons/player/player_pip@3x.png diff --git a/Telegram/Resources/icons/player_pip_close.png b/Telegram/Resources/icons/player/player_pip_close.png similarity index 100% rename from Telegram/Resources/icons/player_pip_close.png rename to Telegram/Resources/icons/player/player_pip_close.png diff --git a/Telegram/Resources/icons/player_pip_close@2x.png b/Telegram/Resources/icons/player/player_pip_close@2x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_close@2x.png rename to Telegram/Resources/icons/player/player_pip_close@2x.png diff --git a/Telegram/Resources/icons/player_pip_close@3x.png b/Telegram/Resources/icons/player/player_pip_close@3x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_close@3x.png rename to Telegram/Resources/icons/player/player_pip_close@3x.png diff --git a/Telegram/Resources/icons/player_pip_enlarge.png b/Telegram/Resources/icons/player/player_pip_enlarge.png similarity index 100% rename from Telegram/Resources/icons/player_pip_enlarge.png rename to Telegram/Resources/icons/player/player_pip_enlarge.png diff --git a/Telegram/Resources/icons/player_pip_enlarge@2x.png b/Telegram/Resources/icons/player/player_pip_enlarge@2x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_enlarge@2x.png rename to Telegram/Resources/icons/player/player_pip_enlarge@2x.png diff --git a/Telegram/Resources/icons/player_pip_enlarge@3x.png b/Telegram/Resources/icons/player/player_pip_enlarge@3x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_enlarge@3x.png rename to Telegram/Resources/icons/player/player_pip_enlarge@3x.png diff --git a/Telegram/Resources/icons/player_pip_pause.png b/Telegram/Resources/icons/player/player_pip_pause.png similarity index 100% rename from Telegram/Resources/icons/player_pip_pause.png rename to Telegram/Resources/icons/player/player_pip_pause.png diff --git a/Telegram/Resources/icons/player_pip_pause@2x.png b/Telegram/Resources/icons/player/player_pip_pause@2x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_pause@2x.png rename to Telegram/Resources/icons/player/player_pip_pause@2x.png diff --git a/Telegram/Resources/icons/player_pip_pause@3x.png b/Telegram/Resources/icons/player/player_pip_pause@3x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_pause@3x.png rename to Telegram/Resources/icons/player/player_pip_pause@3x.png diff --git a/Telegram/Resources/icons/player_pip_play.png b/Telegram/Resources/icons/player/player_pip_play.png similarity index 100% rename from Telegram/Resources/icons/player_pip_play.png rename to Telegram/Resources/icons/player/player_pip_play.png diff --git a/Telegram/Resources/icons/player_pip_play@2x.png b/Telegram/Resources/icons/player/player_pip_play@2x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_play@2x.png rename to Telegram/Resources/icons/player/player_pip_play@2x.png diff --git a/Telegram/Resources/icons/player_pip_play@3x.png b/Telegram/Resources/icons/player/player_pip_play@3x.png similarity index 100% rename from Telegram/Resources/icons/player_pip_play@3x.png rename to Telegram/Resources/icons/player/player_pip_play@3x.png diff --git a/Telegram/Resources/icons/player_play.png b/Telegram/Resources/icons/player/player_play.png similarity index 100% rename from Telegram/Resources/icons/player_play.png rename to Telegram/Resources/icons/player/player_play.png diff --git a/Telegram/Resources/icons/player_play@2x.png b/Telegram/Resources/icons/player/player_play@2x.png similarity index 100% rename from Telegram/Resources/icons/player_play@2x.png rename to Telegram/Resources/icons/player/player_play@2x.png diff --git a/Telegram/Resources/icons/player_play@3x.png b/Telegram/Resources/icons/player/player_play@3x.png similarity index 100% rename from Telegram/Resources/icons/player_play@3x.png rename to Telegram/Resources/icons/player/player_play@3x.png diff --git a/Telegram/Resources/icons/player_repeat.png b/Telegram/Resources/icons/player/player_repeat.png similarity index 100% rename from Telegram/Resources/icons/player_repeat.png rename to Telegram/Resources/icons/player/player_repeat.png diff --git a/Telegram/Resources/icons/player_repeat@2x.png b/Telegram/Resources/icons/player/player_repeat@2x.png similarity index 100% rename from Telegram/Resources/icons/player_repeat@2x.png rename to Telegram/Resources/icons/player/player_repeat@2x.png diff --git a/Telegram/Resources/icons/player_repeat@3x.png b/Telegram/Resources/icons/player/player_repeat@3x.png similarity index 100% rename from Telegram/Resources/icons/player_repeat@3x.png rename to Telegram/Resources/icons/player/player_repeat@3x.png diff --git a/Telegram/Resources/icons/player_volume0.png b/Telegram/Resources/icons/player/player_volume0.png similarity index 100% rename from Telegram/Resources/icons/player_volume0.png rename to Telegram/Resources/icons/player/player_volume0.png diff --git a/Telegram/Resources/icons/player_volume0@2x.png b/Telegram/Resources/icons/player/player_volume0@2x.png similarity index 100% rename from Telegram/Resources/icons/player_volume0@2x.png rename to Telegram/Resources/icons/player/player_volume0@2x.png diff --git a/Telegram/Resources/icons/player_volume0@3x.png b/Telegram/Resources/icons/player/player_volume0@3x.png similarity index 100% rename from Telegram/Resources/icons/player_volume0@3x.png rename to Telegram/Resources/icons/player/player_volume0@3x.png diff --git a/Telegram/Resources/icons/player_volume1.png b/Telegram/Resources/icons/player/player_volume1.png similarity index 100% rename from Telegram/Resources/icons/player_volume1.png rename to Telegram/Resources/icons/player/player_volume1.png diff --git a/Telegram/Resources/icons/player_volume1@2x.png b/Telegram/Resources/icons/player/player_volume1@2x.png similarity index 100% rename from Telegram/Resources/icons/player_volume1@2x.png rename to Telegram/Resources/icons/player/player_volume1@2x.png diff --git a/Telegram/Resources/icons/player_volume1@3x.png b/Telegram/Resources/icons/player/player_volume1@3x.png similarity index 100% rename from Telegram/Resources/icons/player_volume1@3x.png rename to Telegram/Resources/icons/player/player_volume1@3x.png diff --git a/Telegram/Resources/icons/player_volume2.png b/Telegram/Resources/icons/player/player_volume2.png similarity index 100% rename from Telegram/Resources/icons/player_volume2.png rename to Telegram/Resources/icons/player/player_volume2.png diff --git a/Telegram/Resources/icons/player_volume2@2x.png b/Telegram/Resources/icons/player/player_volume2@2x.png similarity index 100% rename from Telegram/Resources/icons/player_volume2@2x.png rename to Telegram/Resources/icons/player/player_volume2@2x.png diff --git a/Telegram/Resources/icons/player_volume2@3x.png b/Telegram/Resources/icons/player/player_volume2@3x.png similarity index 100% rename from Telegram/Resources/icons/player_volume2@3x.png rename to Telegram/Resources/icons/player/player_volume2@3x.png diff --git a/Telegram/Resources/icons/player_volume3.png b/Telegram/Resources/icons/player/player_volume3.png similarity index 100% rename from Telegram/Resources/icons/player_volume3.png rename to Telegram/Resources/icons/player/player_volume3.png diff --git a/Telegram/Resources/icons/player_volume3@2x.png b/Telegram/Resources/icons/player/player_volume3@2x.png similarity index 100% rename from Telegram/Resources/icons/player_volume3@2x.png rename to Telegram/Resources/icons/player/player_volume3@2x.png diff --git a/Telegram/Resources/icons/player_volume3@3x.png b/Telegram/Resources/icons/player/player_volume3@3x.png similarity index 100% rename from Telegram/Resources/icons/player_volume3@3x.png rename to Telegram/Resources/icons/player/player_volume3@3x.png diff --git a/Telegram/Resources/icons/player_volume_off.png b/Telegram/Resources/icons/player/player_volume_off.png similarity index 100% rename from Telegram/Resources/icons/player_volume_off.png rename to Telegram/Resources/icons/player/player_volume_off.png diff --git a/Telegram/Resources/icons/player_volume_off@2x.png b/Telegram/Resources/icons/player/player_volume_off@2x.png similarity index 100% rename from Telegram/Resources/icons/player_volume_off@2x.png rename to Telegram/Resources/icons/player/player_volume_off@2x.png diff --git a/Telegram/Resources/icons/player_volume_off@3x.png b/Telegram/Resources/icons/player/player_volume_off@3x.png similarity index 100% rename from Telegram/Resources/icons/player_volume_off@3x.png rename to Telegram/Resources/icons/player/player_volume_off@3x.png diff --git a/Telegram/Resources/icons/player_volume_on.png b/Telegram/Resources/icons/player/player_volume_on.png similarity index 100% rename from Telegram/Resources/icons/player_volume_on.png rename to Telegram/Resources/icons/player/player_volume_on.png diff --git a/Telegram/Resources/icons/player_volume_on@2x.png b/Telegram/Resources/icons/player/player_volume_on@2x.png similarity index 100% rename from Telegram/Resources/icons/player_volume_on@2x.png rename to Telegram/Resources/icons/player/player_volume_on@2x.png diff --git a/Telegram/Resources/icons/player_volume_on@3x.png b/Telegram/Resources/icons/player/player_volume_on@3x.png similarity index 100% rename from Telegram/Resources/icons/player_volume_on@3x.png rename to Telegram/Resources/icons/player/player_volume_on@3x.png diff --git a/Telegram/Resources/icons/player_volume_small.png b/Telegram/Resources/icons/player/player_volume_small.png similarity index 100% rename from Telegram/Resources/icons/player_volume_small.png rename to Telegram/Resources/icons/player/player_volume_small.png diff --git a/Telegram/Resources/icons/player_volume_small@2x.png b/Telegram/Resources/icons/player/player_volume_small@2x.png similarity index 100% rename from Telegram/Resources/icons/player_volume_small@2x.png rename to Telegram/Resources/icons/player/player_volume_small@2x.png diff --git a/Telegram/Resources/icons/player_volume_small@3x.png b/Telegram/Resources/icons/player/player_volume_small@3x.png similarity index 100% rename from Telegram/Resources/icons/player_volume_small@3x.png rename to Telegram/Resources/icons/player/player_volume_small@3x.png diff --git a/Telegram/Resources/icons/playlist_cancel.png b/Telegram/Resources/icons/player/playlist_cancel.png similarity index 100% rename from Telegram/Resources/icons/playlist_cancel.png rename to Telegram/Resources/icons/player/playlist_cancel.png diff --git a/Telegram/Resources/icons/playlist_cancel@2x.png b/Telegram/Resources/icons/player/playlist_cancel@2x.png similarity index 100% rename from Telegram/Resources/icons/playlist_cancel@2x.png rename to Telegram/Resources/icons/player/playlist_cancel@2x.png diff --git a/Telegram/Resources/icons/playlist_cancel@3x.png b/Telegram/Resources/icons/player/playlist_cancel@3x.png similarity index 100% rename from Telegram/Resources/icons/playlist_cancel@3x.png rename to Telegram/Resources/icons/player/playlist_cancel@3x.png diff --git a/Telegram/Resources/icons/playlist_download.png b/Telegram/Resources/icons/player/playlist_download.png similarity index 100% rename from Telegram/Resources/icons/playlist_download.png rename to Telegram/Resources/icons/player/playlist_download.png diff --git a/Telegram/Resources/icons/playlist_download@2x.png b/Telegram/Resources/icons/player/playlist_download@2x.png similarity index 100% rename from Telegram/Resources/icons/playlist_download@2x.png rename to Telegram/Resources/icons/player/playlist_download@2x.png diff --git a/Telegram/Resources/icons/playlist_download@3x.png b/Telegram/Resources/icons/player/playlist_download@3x.png similarity index 100% rename from Telegram/Resources/icons/playlist_download@3x.png rename to Telegram/Resources/icons/player/playlist_download@3x.png diff --git a/Telegram/Resources/icons/playlist_pause.png b/Telegram/Resources/icons/player/playlist_pause.png similarity index 100% rename from Telegram/Resources/icons/playlist_pause.png rename to Telegram/Resources/icons/player/playlist_pause.png diff --git a/Telegram/Resources/icons/playlist_pause@2x.png b/Telegram/Resources/icons/player/playlist_pause@2x.png similarity index 100% rename from Telegram/Resources/icons/playlist_pause@2x.png rename to Telegram/Resources/icons/player/playlist_pause@2x.png diff --git a/Telegram/Resources/icons/playlist_pause@3x.png b/Telegram/Resources/icons/player/playlist_pause@3x.png similarity index 100% rename from Telegram/Resources/icons/playlist_pause@3x.png rename to Telegram/Resources/icons/player/playlist_pause@3x.png diff --git a/Telegram/Resources/icons/playlist_play.png b/Telegram/Resources/icons/player/playlist_play.png similarity index 100% rename from Telegram/Resources/icons/playlist_play.png rename to Telegram/Resources/icons/player/playlist_play.png diff --git a/Telegram/Resources/icons/playlist_play@2x.png b/Telegram/Resources/icons/player/playlist_play@2x.png similarity index 100% rename from Telegram/Resources/icons/playlist_play@2x.png rename to Telegram/Resources/icons/player/playlist_play@2x.png diff --git a/Telegram/Resources/icons/playlist_play@3x.png b/Telegram/Resources/icons/player/playlist_play@3x.png similarity index 100% rename from Telegram/Resources/icons/playlist_play@3x.png rename to Telegram/Resources/icons/player/playlist_play@3x.png diff --git a/Telegram/Resources/icons/playlist_shadow.png b/Telegram/Resources/icons/player/playlist_shadow.png similarity index 100% rename from Telegram/Resources/icons/playlist_shadow.png rename to Telegram/Resources/icons/player/playlist_shadow.png diff --git a/Telegram/Resources/icons/playlist_shadow@2x.png b/Telegram/Resources/icons/player/playlist_shadow@2x.png similarity index 100% rename from Telegram/Resources/icons/playlist_shadow@2x.png rename to Telegram/Resources/icons/player/playlist_shadow@2x.png diff --git a/Telegram/Resources/icons/playlist_shadow@3x.png b/Telegram/Resources/icons/player/playlist_shadow@3x.png similarity index 100% rename from Telegram/Resources/icons/playlist_shadow@3x.png rename to Telegram/Resources/icons/player/playlist_shadow@3x.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed0.5.png b/Telegram/Resources/icons/player/voice_speed/voice_speed0.5.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed0.5.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed0.5.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed0.5@2x.png b/Telegram/Resources/icons/player/voice_speed/voice_speed0.5@2x.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed0.5@2x.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed0.5@2x.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed0.5@3x.png b/Telegram/Resources/icons/player/voice_speed/voice_speed0.5@3x.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed0.5@3x.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed0.5@3x.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed1.5.png b/Telegram/Resources/icons/player/voice_speed/voice_speed1.5.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed1.5.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed1.5.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed1.5@2x.png b/Telegram/Resources/icons/player/voice_speed/voice_speed1.5@2x.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed1.5@2x.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed1.5@2x.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed1.5@3x.png b/Telegram/Resources/icons/player/voice_speed/voice_speed1.5@3x.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed1.5@3x.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed1.5@3x.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed2.png b/Telegram/Resources/icons/player/voice_speed/voice_speed2.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed2.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed2.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed2@2x.png b/Telegram/Resources/icons/player/voice_speed/voice_speed2@2x.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed2@2x.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed2@2x.png diff --git a/Telegram/Resources/icons/voice_speed/voice_speed2@3x.png b/Telegram/Resources/icons/player/voice_speed/voice_speed2@3x.png similarity index 100% rename from Telegram/Resources/icons/voice_speed/voice_speed2@3x.png rename to Telegram/Resources/icons/player/voice_speed/voice_speed2@3x.png diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 2304220f9..ab91473f1 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -51,7 +51,7 @@ mediaPlayerRepeatButton: IconButton { height: 30px; icon: icon { - { "player_repeat", mediaPlayerActiveFg, point(9px, 11px) } + { "player/player_repeat", mediaPlayerActiveFg, point(9px, 11px) } }; iconPosition: point(0px, 0px); @@ -62,14 +62,14 @@ mediaPlayerRepeatButton: IconButton { } } mediaPlayerRepeatDisabledIcon: icon { - { "player_repeat", menuIconFg, point(9px, 11px)} + { "player/player_repeat", menuIconFg, point(9px, 11px)} }; mediaPlayerRepeatDisabledIconOver: icon { - { "player_repeat", menuIconFgOver, point(9px, 11px)} + { "player/player_repeat", menuIconFgOver, point(9px, 11px)} }; mediaPlayerRepeatDisabledRippleBg: windowBgOver; mediaPlayerRepeatInactiveIcon: icon { - { "player_repeat", mediaPlayerInactiveFg, point(9px, 11px)} + { "player/player_repeat", mediaPlayerInactiveFg, point(9px, 11px)} }; mediaPlayerSpeedButton: IconButton { @@ -77,7 +77,7 @@ mediaPlayerSpeedButton: IconButton { height: 30px; icon: icon { - { "voice_speed/voice_speed2", mediaPlayerActiveFg } + { "player/voice_speed/voice_speed2", mediaPlayerActiveFg } }; iconPosition: point(3px, 10px); @@ -88,28 +88,28 @@ mediaPlayerSpeedButton: IconButton { } } mediaPlayerSpeedDisabledIcon: icon { - { "voice_speed/voice_speed2", menuIconFg } + { "player/voice_speed/voice_speed2", menuIconFg } }; mediaPlayerSpeedDisabledIconOver: icon { - { "voice_speed/voice_speed2", menuIconFgOver } + { "player/voice_speed/voice_speed2", menuIconFgOver } }; mediaPlayerSpeedSlowIcon: icon { - { "voice_speed/voice_speed0.5", mediaPlayerActiveFg } + { "player/voice_speed/voice_speed0.5", mediaPlayerActiveFg } }; mediaPlayerSpeedSlowDisabledIcon: icon { - { "voice_speed/voice_speed0.5", menuIconFg } + { "player/voice_speed/voice_speed0.5", menuIconFg } }; mediaPlayerSpeedSlowDisabledIconOver: icon { - { "voice_speed/voice_speed0.5", menuIconFgOver } + { "player/voice_speed/voice_speed0.5", menuIconFgOver } }; mediaPlayerSpeedFastIcon: icon { - { "voice_speed/voice_speed1.5", mediaPlayerActiveFg } + { "player/voice_speed/voice_speed1.5", mediaPlayerActiveFg } }; mediaPlayerSpeedFastDisabledIcon: icon { - { "voice_speed/voice_speed1.5", menuIconFg } + { "player/voice_speed/voice_speed1.5", menuIconFg } }; mediaPlayerSpeedFastDisabledIconOver: icon { - { "voice_speed/voice_speed1.5", menuIconFgOver } + { "player/voice_speed/voice_speed1.5", menuIconFgOver } }; mediaPlayerSpeedDisabledRippleBg: windowBgOver; @@ -119,19 +119,19 @@ mediaPlayerPopupMenu: PopupMenu(defaultPopupMenu) { itemPadding: margins(34px, 8px, 17px, 7px); } } -mediaPlayerMenuCheck: icon {{ "player_check", mediaPlayerActiveFg }}; +mediaPlayerMenuCheck: icon {{ "player/player_check", mediaPlayerActiveFg }}; mediaPlayerVolumeIcon0: icon { - { "player_volume0", mediaPlayerActiveFg }, + { "player/player_volume0", mediaPlayerActiveFg }, }; mediaPlayerVolumeIcon1: icon { - { "player_volume1", mediaPlayerActiveFg }, + { "player/player_volume1", mediaPlayerActiveFg }, }; mediaPlayerVolumeIcon2: icon { - { "player_volume2", mediaPlayerActiveFg }, + { "player/player_volume2", mediaPlayerActiveFg }, }; mediaPlayerVolumeIcon3: icon { - { "player_volume3", mediaPlayerActiveFg }, + { "player/player_volume3", mediaPlayerActiveFg }, }; mediaPlayerVolumeToggle: IconButton { width: 31px; @@ -149,43 +149,37 @@ mediaPlayerVolumeToggle: IconButton { mediaPlayerVolumeMargin: 10px; mediaPlayerVolumeSize: size(27px, 100px); -mediaPlayerPanelPinButton: IconButton(mediaPlayerRepeatButton) { - icon: icon { - { "player_panel_pin", mediaPlayerActiveFg, point(9px, 11px) } - }; -} - mediaPlayerNextButton: IconButton(mediaPlayerRepeatButton) { width: 25px; icon: icon { - { "player_next", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, + { "player/player_next", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, }; rippleAreaPosition: point(0px, 5px); } mediaPlayerNextDisabledIcon: icon { - { "player_next", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, + { "player/player_next", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, }; mediaPlayerPreviousButton: IconButton(mediaPlayerNextButton) { icon: icon { - { "player_next-flip_horizontal", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, + { "player/player_next-flip_horizontal", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, }; rippleAreaPosition: point(1px, 5px); } mediaPlayerPreviousDisabledIcon: icon { - { "player_next-flip_horizontal", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, + { "player/player_next-flip_horizontal", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, }; -touchBarIconPlayerClose: icon {{ "player_close", windowFg }}; +touchBarIconPlayerClose: icon {{ "player/player_close", windowFg }}; touchBarIconPlayerPlay: icon {{ "media_play", windowFg }}; touchBarIconPlayerPause: icon {{ "media_pause", windowFg }}; -touchBarIconPlayerNext: icon {{ "player_next", windowFg }}; -touchBarIconPlayerPrevious: icon {{ "player_next-flip_horizontal", windowFg }}; +touchBarIconPlayerNext: icon {{ "player/player_next", windowFg }}; +touchBarIconPlayerPrevious: icon {{ "player/player_next-flip_horizontal", windowFg }}; mediaPlayerClose: IconButton(mediaPlayerRepeatButton) { width: 37px; - icon: icon {{ "player_close", menuIconFg, point(10px, 12px) }}; - iconOver: icon {{ "player_close", menuIconFgOver, point(10px, 12px) }}; + icon: icon {{ "player/player_close", menuIconFg, point(10px, 12px) }}; + iconOver: icon {{ "player/player_close", menuIconFgOver, point(10px, 12px) }}; rippleAreaPosition: point(3px, 5px); ripple: RippleAnimation(defaultRippleAnimation) { @@ -224,22 +218,22 @@ mediaPlayerCoverHeight: 102px; mediaPlayerPanelClose: IconButton(mediaPlayerClose) { width: 43px; height: 28px; - icon: icon {{ "player_close", menuIconFg, point(16px, 14px) }}; - iconOver: icon {{ "player_close", menuIconFgOver, point(16px, 14px) }}; + icon: icon {{ "player/player_close", menuIconFg, point(16px, 14px) }}; + iconOver: icon {{ "player/player_close", menuIconFgOver, point(16px, 14px) }}; } mediaPlayerPanelNextButton: IconButton(mediaPlayerRepeatButton) { width: 37px; - icon: icon {{ "player_panel_next", mediaPlayerActiveFg, point(10px, 10px) }}; + icon: icon {{ "player/player_panel_next", mediaPlayerActiveFg, point(10px, 10px) }}; } mediaPlayerPanelNextDisabledIcon: icon { - { "player_panel_next", mediaPlayerInactiveFg, point(10px, 10px) }, + { "player/player_panel_next", mediaPlayerInactiveFg, point(10px, 10px) }, }; mediaPlayerPanelPreviousButton: IconButton(mediaPlayerPanelNextButton) { - icon: icon {{ "player_panel_next-flip_horizontal", mediaPlayerActiveFg, point(10px, 10px) }}; + icon: icon {{ "player/player_panel_next-flip_horizontal", mediaPlayerActiveFg, point(10px, 10px) }}; } mediaPlayerPanelPreviousDisabledIcon: icon { - { "player_panel_next-flip_horizontal", mediaPlayerInactiveFg, point(10px, 10px) }, + { "player/player_panel_next-flip_horizontal", mediaPlayerInactiveFg, point(10px, 10px) }, }; mediaPlayerPanelPadding: 16px; @@ -264,7 +258,7 @@ mediaPlayerScroll: ScrollArea(defaultSolidScroll) { mediaPlayerListHeightMax: 280px; mediaPlayerListMarginBottom: 10px; mediaPlayerScrollShadow: Shadow { - bottom: icon {{ "playlist_shadow", windowShadowFg }}; + bottom: icon {{ "player/playlist_shadow", windowShadowFg }}; extend: margins(2px, 2px, 2px, 2px); } diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 81591e511..9409e3cf9 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -46,12 +46,12 @@ mediaviewPlayButton: IconButton(mediaviewControlsButton) { height: 42px; rippleAreaSize: 42px; - icon: icon {{ "player_play", mediaviewPlaybackIconFg }}; - iconOver: icon {{ "player_play", mediaviewPlaybackIconFgOver }}; + icon: icon {{ "player/player_play", mediaviewPlaybackIconFg }}; + iconOver: icon {{ "player/player_play", mediaviewPlaybackIconFgOver }}; iconPosition: point(9px, 9px); } -mediaviewPauseIcon: icon {{ "player_pause", mediaviewPlaybackIconFg }}; -mediaviewPauseIconOver: icon {{ "player_pause", mediaviewPlaybackIconFgOver }}; +mediaviewPauseIcon: icon {{ "player/player_pause", mediaviewPlaybackIconFg }}; +mediaviewPauseIconOver: icon {{ "player/player_pause", mediaviewPlaybackIconFgOver }}; mediaviewButtonsTop: 7px; @@ -60,34 +60,34 @@ mediaviewMenuToggle: IconButton(mediaviewControlsButton) { width: 34px; height: 34px; rippleAreaSize: 34px; - icon: icon {{ "player_more", mediaviewPlaybackIconFg }}; - iconOver: icon {{ "player_more", mediaviewPlaybackIconFgOver }}; + icon: icon {{ "player/player_more", mediaviewPlaybackIconFg }}; + iconOver: icon {{ "player/player_more", mediaviewPlaybackIconFgOver }}; iconPosition: point(5px, 5px); } mediaviewPipButtonSkip: 5px; mediaviewPipButton: IconButton(mediaviewMenuToggle) { - icon: icon {{ "player_pip", mediaviewPlaybackIconFg }}; - iconOver: icon {{ "player_pip", mediaviewPlaybackIconFgOver }}; + icon: icon {{ "player/player_pip", mediaviewPlaybackIconFg }}; + iconOver: icon {{ "player/player_pip", mediaviewPlaybackIconFgOver }}; } mediaviewFullScreenButtonSkip: 8px; mediaviewFullScreenButton: IconButton(mediaviewMenuToggle) { - icon: icon {{ "player_fullscreen", mediaviewPlaybackIconFg }}; - iconOver: icon {{ "player_fullscreen", mediaviewPlaybackIconFgOver }}; + icon: icon {{ "player/player_fullscreen", mediaviewPlaybackIconFg }}; + iconOver: icon {{ "player/player_fullscreen", mediaviewPlaybackIconFgOver }}; } -mediaviewFullScreenOutIcon: icon {{ "player_minimize", mediaviewPlaybackIconFg }}; -mediaviewFullScreenOutIconOver: icon {{ "player_minimize", mediaviewPlaybackIconFgOver }}; +mediaviewFullScreenOutIcon: icon {{ "player/player_minimize", mediaviewPlaybackIconFg }}; +mediaviewFullScreenOutIconOver: icon {{ "player/player_minimize", mediaviewPlaybackIconFgOver }}; mediaviewVolumeWidth: 75px; mediaviewControllerRadius: 9px; -mediaviewVolumeIcon0: icon {{ "player_volume_off", mediaviewPlaybackIconFg }}; -mediaviewVolumeIcon0Over: icon {{ "player_volume_off", mediaviewPlaybackIconFgOver }}; -mediaviewVolumeIcon1: icon {{ "player_volume_small", mediaviewPlaybackIconFg }}; -mediaviewVolumeIcon1Over: icon {{ "player_volume_small", mediaviewPlaybackIconFgOver }}; -mediaviewVolumeIcon2: icon {{ "player_volume_on", mediaviewPlaybackIconFg }}; -mediaviewVolumeIcon2Over: icon {{ "player_volume_on", mediaviewPlaybackIconFgOver }}; +mediaviewVolumeIcon0: icon {{ "player/player_volume_off", mediaviewPlaybackIconFg }}; +mediaviewVolumeIcon0Over: icon {{ "player/player_volume_off", mediaviewPlaybackIconFgOver }}; +mediaviewVolumeIcon1: icon {{ "player/player_volume_small", mediaviewPlaybackIconFg }}; +mediaviewVolumeIcon1Over: icon {{ "player/player_volume_small", mediaviewPlaybackIconFgOver }}; +mediaviewVolumeIcon2: icon {{ "player/player_volume_on", mediaviewPlaybackIconFg }}; +mediaviewVolumeIcon2Over: icon {{ "player/player_volume_on", mediaviewPlaybackIconFgOver }}; mediaviewVolumeTop: 10px; mediaviewVolumeToggleSkip: 11px; mediaviewVolumeToggle: IconButton(mediaviewControlsButton) { @@ -205,7 +205,7 @@ mediaviewControlsPopupMenu: PopupMenu(defaultPopupMenu) { menu: mediaviewControlsMenu; animation: mediaviewControlsPanelAnimation; } -mediaviewMenuCheck: icon {{ "player_check", mediaviewPlaybackProgressFg }}; +mediaviewMenuCheck: icon {{ "player/player_check", mediaviewPlaybackProgressFg }}; mediaviewSaveMsgCheck: icon {{ "mediaview_save_check", mediaviewSaveMsgFg }}; mediaviewSaveMsgPadding: margins(55px, 19px, 29px, 20px); @@ -293,19 +293,19 @@ pipPlaybackWide: 4px; pipPlaybackSkip: 4px; pipPlaybackTextSkip: 6px; pipPlaybackFont: font(11px); -pipPlayIcon: icon {{ "player_pip_play", mediaviewPipControlsFg }}; -pipPlayIconOver: icon {{ "player_pip_play", mediaviewPipControlsFgOver }}; -pipPauseIcon: icon {{ "player_pip_pause", mediaviewPipControlsFg }}; -pipPauseIconOver: icon {{ "player_pip_pause", mediaviewPipControlsFgOver }}; -pipCloseIcon: icon {{ "player_pip_close", mediaviewPipControlsFg }}; -pipCloseIconOver: icon {{ "player_pip_close", mediaviewPipControlsFgOver }}; -pipEnlargeIcon: icon {{ "player_pip_enlarge", mediaviewPipControlsFg }}; -pipEnlargeIconOver: icon {{ "player_pip_enlarge", mediaviewPipControlsFgOver }}; -pipVolumeIcon0: icon {{ "player_volume_off", mediaviewPipControlsFg }}; -pipVolumeIcon0Over: icon {{ "player_volume_off", mediaviewPipControlsFgOver }}; -pipVolumeIcon1: icon {{ "player_volume_small", mediaviewPipControlsFg }}; -pipVolumeIcon1Over: icon {{ "player_volume_small", mediaviewPipControlsFgOver }}; -pipVolumeIcon2: icon {{ "player_volume_on", mediaviewPipControlsFg }}; -pipVolumeIcon2Over: icon {{ "player_volume_on", mediaviewPipControlsFgOver }}; +pipPlayIcon: icon {{ "player/player_pip_play", mediaviewPipControlsFg }}; +pipPlayIconOver: icon {{ "player/player_pip_play", mediaviewPipControlsFgOver }}; +pipPauseIcon: icon {{ "player/player_pip_pause", mediaviewPipControlsFg }}; +pipPauseIconOver: icon {{ "player/player_pip_pause", mediaviewPipControlsFgOver }}; +pipCloseIcon: icon {{ "player/player_pip_close", mediaviewPipControlsFg }}; +pipCloseIconOver: icon {{ "player/player_pip_close", mediaviewPipControlsFgOver }}; +pipEnlargeIcon: icon {{ "player/player_pip_enlarge", mediaviewPipControlsFg }}; +pipEnlargeIconOver: icon {{ "player/player_pip_enlarge", mediaviewPipControlsFgOver }}; +pipVolumeIcon0: icon {{ "player/player_volume_off", mediaviewPipControlsFg }}; +pipVolumeIcon0Over: icon {{ "player/player_volume_off", mediaviewPipControlsFgOver }}; +pipVolumeIcon1: icon {{ "player/player_volume_small", mediaviewPipControlsFg }}; +pipVolumeIcon1Over: icon {{ "player/player_volume_small", mediaviewPipControlsFgOver }}; +pipVolumeIcon2: icon {{ "player/player_volume_on", mediaviewPipControlsFg }}; +pipVolumeIcon2Over: icon {{ "player/player_volume_on", mediaviewPipControlsFgOver }}; speedSliderDividerSize: size(2px, 8px); diff --git a/Telegram/SourceFiles/overview/overview.style b/Telegram/SourceFiles/overview/overview.style index ac4423d7f..88c972017 100644 --- a/Telegram/SourceFiles/overview/overview.style +++ b/Telegram/SourceFiles/overview/overview.style @@ -73,23 +73,23 @@ overviewFileExtTop: 24px; overviewFileExtFg: windowFgActive; overviewFileExtFont: font(18px semibold); -overviewVoicePause: icon {{ "playlist_pause", historyFileInIconFg }}; -overviewVoicePauseSelected: icon {{ "playlist_pause", historyFileInIconFgSelected }}; -overviewVoicePlay: icon {{ "playlist_play", historyFileInIconFg }}; -overviewVoicePlaySelected: icon {{ "playlist_play", historyFileInIconFgSelected }}; -overviewVoiceCancel: icon {{ "playlist_cancel", historyFileInIconFg }}; -overviewVoiceCancelSelected: icon {{ "playlist_cancel", historyFileInIconFgSelected }}; -overviewVoiceDownload: icon {{ "playlist_download", historyFileInIconFg }}; -overviewVoiceDownloadSelected: icon {{ "playlist_download", historyFileInIconFgSelected }}; +overviewVoicePause: icon {{ "player/playlist_pause", historyFileInIconFg }}; +overviewVoicePauseSelected: icon {{ "player/playlist_pause", historyFileInIconFgSelected }}; +overviewVoicePlay: icon {{ "player/playlist_play", historyFileInIconFg }}; +overviewVoicePlaySelected: icon {{ "player/playlist_play", historyFileInIconFgSelected }}; +overviewVoiceCancel: icon {{ "player/playlist_cancel", historyFileInIconFg }}; +overviewVoiceCancelSelected: icon {{ "player/playlist_cancel", historyFileInIconFgSelected }}; +overviewVoiceDownload: icon {{ "player/playlist_download", historyFileInIconFg }}; +overviewVoiceDownloadSelected: icon {{ "player/playlist_download", historyFileInIconFgSelected }}; -overviewSongPause: icon {{ "playlist_pause", historyFileThumbIconFg }}; -overviewSongPauseSelected: icon {{ "playlist_pause", historyFileThumbIconFgSelected }}; -overviewSongPlay: icon {{ "playlist_play", historyFileThumbIconFg }}; -overviewSongPlaySelected: icon {{ "playlist_play", historyFileThumbIconFgSelected }}; -overviewSongCancel: icon {{ "playlist_cancel", historyFileThumbIconFg }}; -overviewSongCancelSelected: icon {{ "playlist_cancel", historyFileThumbIconFgSelected }}; -overviewSongDownload: icon {{ "playlist_download", historyFileThumbIconFg }}; -overviewSongDownloadSelected: icon {{ "playlist_download", historyFileThumbIconFgSelected }}; +overviewSongPause: icon {{ "player/playlist_pause", historyFileThumbIconFg }}; +overviewSongPauseSelected: icon {{ "player/playlist_pause", historyFileThumbIconFgSelected }}; +overviewSongPlay: icon {{ "player/playlist_play", historyFileThumbIconFg }}; +overviewSongPlaySelected: icon {{ "player/playlist_play", historyFileThumbIconFgSelected }}; +overviewSongCancel: icon {{ "player/playlist_cancel", historyFileThumbIconFg }}; +overviewSongCancelSelected: icon {{ "player/playlist_cancel", historyFileThumbIconFgSelected }}; +overviewSongDownload: icon {{ "player/playlist_download", historyFileThumbIconFg }}; +overviewSongDownloadSelected: icon {{ "player/playlist_download", historyFileThumbIconFgSelected }}; overviewSmallCancel: icon {{ "history_audio_cancel", historyFileInIconFg }}; overviewSmallCancelSelected: icon {{ "history_audio_cancel", historyFileInIconFgSelected }}; overviewSmallDownload: icon {{ "history_audio_download", historyFileInIconFg }}; diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 7141d057c..e856d8158 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -782,10 +782,10 @@ historyGroupAboutSkip: 8px; historyVideoDownloadSize: 44px; historyVideoMuteSize: 22px; -historyVideoCancel: icon {{ "playlist_cancel", historyFileThumbIconFg }}; -historyVideoCancelSelected: icon {{ "playlist_cancel", historyFileThumbIconFgSelected }}; -historyVideoDownload: icon {{ "playlist_download", historyFileThumbIconFg }}; -historyVideoDownloadSelected: icon {{ "playlist_download", historyFileThumbIconFgSelected }}; +historyVideoCancel: icon {{ "player/playlist_cancel", historyFileThumbIconFg }}; +historyVideoCancelSelected: icon {{ "player/playlist_cancel", historyFileThumbIconFgSelected }}; +historyVideoDownload: icon {{ "player/playlist_download", historyFileThumbIconFg }}; +historyVideoDownloadSelected: icon {{ "player/playlist_download", historyFileThumbIconFgSelected }}; historyVideoRadialLine: msgFileRadialLine; historyAudioDownloadSize: 20px; From a2bf1544dff98cc626dc05085b0c8bcca72efded Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 20:11:39 +0400 Subject: [PATCH 016/180] Remove Q_OBJECT from VolumeWidget. --- .../player/media_player_volume_controller.cpp | 43 +++++++++---------- .../player/media_player_volume_controller.h | 19 ++++---- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index a453f74cf..9c524e1c7 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/continuous_sliders.h" #include "ui/ui_utility.h" #include "ui/cached_round_corners.h" -#include "base/object_ptr.h" #include "mainwindow.h" #include "main/main_session.h" #include "window/window_session_controller.h" @@ -81,16 +80,12 @@ VolumeWidget::VolumeWidget( QWidget *parent, not_null controller) : RpWidget(parent) -, _controller(this, controller) { +, _controller(this, controller) +, _hideTimer([=] { startHide(); }) +, _showTimer([=] { startShow(); }) { hide(); _controller->setIsVertical(true); - _hideTimer.setSingleShot(true); - connect(&_hideTimer, SIGNAL(timeout()), this, SLOT(onHideStart())); - - _showTimer.setSingleShot(true); - connect(&_showTimer, SIGNAL(timeout()), this, SLOT(onShowStart())); - macWindowDeactivateEvents( ) | rpl::filter([=] { return !isHidden(); @@ -150,44 +145,44 @@ void VolumeWidget::paintEvent(QPaintEvent *e) { } void VolumeWidget::enterEventHook(QEnterEvent *e) { - _hideTimer.stop(); + _hideTimer.cancel(); if (_a_appearance.animating()) { - onShowStart(); + startShow(); } else { - _showTimer.start(0); + _showTimer.callOnce(0); } return RpWidget::enterEventHook(e); } void VolumeWidget::leaveEventHook(QEvent *e) { - _showTimer.stop(); + _showTimer.cancel(); if (_a_appearance.animating()) { - onHideStart(); + startHide(); } else { - _hideTimer.start(300); + _hideTimer.callOnce(300); } return RpWidget::leaveEventHook(e); } void VolumeWidget::otherEnter() { - _hideTimer.stop(); + _hideTimer.cancel(); if (_a_appearance.animating()) { - onShowStart(); + startShow(); } else { - _showTimer.start(0); + _showTimer.callOnce(0); } } void VolumeWidget::otherLeave() { - _showTimer.stop(); + _showTimer.cancel(); if (_a_appearance.animating()) { - onHideStart(); + startHide(); } else { - _hideTimer.start(0); + _hideTimer.callOnce(0); } } -void VolumeWidget::onShowStart() { +void VolumeWidget::startShow() { if (isHidden()) { show(); } else if (!_hiding) { @@ -197,8 +192,10 @@ void VolumeWidget::onShowStart() { startAnimation(); } -void VolumeWidget::onHideStart() { - if (_hiding) return; +void VolumeWidget::startHide() { + if (_hiding) { + return; + } _hiding = true; startAnimation(); diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.h b/Telegram/SourceFiles/media/player/media_player_volume_controller.h index 386910817..bda9d80fe 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.h +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.h @@ -10,8 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/rp_widget.h" #include "base/object_ptr.h" - -#include +#include "base/timer.h" namespace Ui { class IconButton; @@ -44,9 +43,7 @@ private: }; -class VolumeWidget : public Ui::RpWidget { - Q_OBJECT - +class VolumeWidget final : public Ui::RpWidget { public: VolumeWidget( QWidget *parent, @@ -64,11 +61,10 @@ protected: bool eventFilter(QObject *obj, QEvent *e) override; -private Q_SLOTS: - void onShowStart(); - void onHideStart(); - private: + void startHide(); + void startShow(); + void otherEnter(); void otherLeave(); @@ -81,10 +77,11 @@ private: QPixmap _cache; Ui::Animations::Simple _a_appearance; - QTimer _hideTimer, _showTimer; - object_ptr _controller; + base::Timer _hideTimer; + base::Timer _showTimer; + }; } // namespace Player From 8a1140c09f156e9aa883841a0d866b280ed63671 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 21:02:12 +0400 Subject: [PATCH 017/180] Extract Media::Player::Dropdown widget. --- Telegram/SourceFiles/mainwidget.cpp | 6 +- Telegram/SourceFiles/mainwidget.h | 4 +- .../media/player/media_player_dropdown.cpp | 169 ++++++++++++++++ .../media/player/media_player_dropdown.h | 52 +++++ .../player/media_player_volume_controller.cpp | 186 +++--------------- .../player/media_player_volume_controller.h | 54 +---- .../media/player/media_player_widget.cpp | 18 +- .../media/player/media_player_widget.h | 6 +- Telegram/cmake/td_ui.cmake | 3 + 9 files changed, 274 insertions(+), 224 deletions(-) create mode 100644 Telegram/SourceFiles/media/player/media_player_dropdown.cpp create mode 100644 Telegram/SourceFiles/media/player/media_player_dropdown.h diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 4f1a36161..5de7f7355 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -80,6 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/player/media_player_panel.h" #include "media/player/media_player_widget.h" +#include "media/player/media_player_dropdown.h" #include "media/player/media_player_volume_controller.h" #include "media/player/media_player_instance.h" #include "media/player/media_player_float.h" @@ -843,7 +844,10 @@ void MainWidget::createPlayer() { not_null item) { _controller->showPeerHistoryAtItem(item); }); - _playerVolume.create(this, _controller); + _playerVolume.create(this); + Media::Player::PrepareVolumeDropdown( + _playerVolume.data(), + _controller); _player->entity()->volumeWidgetCreated(_playerVolume); orderWidgets(); if (_a_show.animating()) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index e8ecf4a11..beb97010d 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -55,7 +55,7 @@ class Widget; namespace Media { namespace Player { class Widget; -class VolumeWidget; +class Dropdown; class Panel; struct TrackState; } // namespace Player @@ -367,7 +367,7 @@ private: object_ptr> _player = { nullptr }; - object_ptr _playerVolume = { nullptr }; + object_ptr _playerVolume = { nullptr }; object_ptr _playerPlaylist; bool _playerUsingPanel = false; diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp new file mode 100644 index 000000000..855c8e46e --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp @@ -0,0 +1,169 @@ +/* +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 "media/player/media_player_dropdown.h" + +#include "ui/cached_round_corners.h" +#include "ui/widgets/shadow.h" +#include "styles/style_media_player.h" +#include "styles/style_widgets.h" + +namespace Media::Player { + +Dropdown::Dropdown(QWidget *parent) +: RpWidget(parent) +, _hideTimer([=] { startHide(); }) +, _showTimer([=] { startShow(); }) { + hide(); + + macWindowDeactivateEvents( + ) | rpl::filter([=] { + return !isHidden(); + }) | rpl::start_with_next([=] { + leaveEvent(nullptr); + }, lifetime()); + + hide(); + auto margin = getMargin(); + resize(margin.left() + st::mediaPlayerVolumeSize.width() + margin.right(), margin.top() + st::mediaPlayerVolumeSize.height() + margin.bottom()); +} + +QMargins Dropdown::getMargin() const { + const auto top = st::mediaPlayerHeight + + st::lineWidth + - st::mediaPlayerPlayTop + - st::mediaPlayerVolumeToggle.height; + return QMargins(st::mediaPlayerVolumeMargin, top, st::mediaPlayerVolumeMargin, st::mediaPlayerVolumeMargin); +} + +bool Dropdown::overlaps(const QRect &globalRect) { + if (isHidden() || _a_appearance.animating()) return false; + + return rect().marginsRemoved(getMargin()).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); +} + +void Dropdown::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_cache.isNull()) { + bool animating = _a_appearance.animating(); + if (animating) { + p.setOpacity(_a_appearance.value(_hiding ? 0. : 1.)); + } else if (_hiding || isHidden()) { + hidingFinished(); + return; + } + p.drawPixmap(0, 0, _cache); + if (!animating) { + showChildren(); + _cache = QPixmap(); + } + return; + } + + // draw shadow + auto shadowedRect = rect().marginsRemoved(getMargin()); + auto shadowedSides = RectPart::Left | RectPart::Right | RectPart::Bottom; + Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides); + auto parts = RectPart::NoTopBottom | RectPart::FullBottom; + Ui::FillRoundRect(p, QRect(shadowedRect.x(), -st::roundRadiusSmall, shadowedRect.width(), shadowedRect.y() + shadowedRect.height() + st::roundRadiusSmall), st::menuBg, Ui::MenuCorners, nullptr, parts); +} + +void Dropdown::enterEventHook(QEnterEvent *e) { + _hideTimer.cancel(); + if (_a_appearance.animating()) { + startShow(); + } else { + _showTimer.callOnce(0); + } + return RpWidget::enterEventHook(e); +} + +void Dropdown::leaveEventHook(QEvent *e) { + _showTimer.cancel(); + if (_a_appearance.animating()) { + startHide(); + } else { + _hideTimer.callOnce(300); + } + return RpWidget::leaveEventHook(e); +} + +void Dropdown::otherEnter() { + _hideTimer.cancel(); + if (_a_appearance.animating()) { + startShow(); + } else { + _showTimer.callOnce(0); + } +} + +void Dropdown::otherLeave() { + _showTimer.cancel(); + if (_a_appearance.animating()) { + startHide(); + } else { + _hideTimer.callOnce(0); + } +} + +void Dropdown::startShow() { + if (isHidden()) { + show(); + } else if (!_hiding) { + return; + } + _hiding = false; + startAnimation(); +} + +void Dropdown::startHide() { + if (_hiding) { + return; + } + + _hiding = true; + startAnimation(); +} + +void Dropdown::startAnimation() { + if (_cache.isNull()) { + showChildren(); + _cache = Ui::GrabWidget(this); + } + hideChildren(); + _a_appearance.start( + [=] { appearanceCallback(); }, + _hiding ? 1. : 0., + _hiding ? 0. : 1., + st::defaultInnerDropdown.duration); +} + +void Dropdown::appearanceCallback() { + if (!_a_appearance.animating() && _hiding) { + _hiding = false; + hidingFinished(); + } else { + update(); + } +} + +void Dropdown::hidingFinished() { + hide(); + _cache = QPixmap(); +} + +bool Dropdown::eventFilter(QObject *obj, QEvent *e) { + if (e->type() == QEvent::Enter) { + otherEnter(); + } else if (e->type() == QEvent::Leave) { + otherLeave(); + } + return false; +} + +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.h b/Telegram/SourceFiles/media/player/media_player_dropdown.h new file mode 100644 index 000000000..67e1dfe54 --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.h @@ -0,0 +1,52 @@ +/* +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 "ui/rp_widget.h" +#include "ui/effects/animations.h" +#include "base/timer.h" + +namespace Media::Player { + +class Dropdown final : public Ui::RpWidget { +public: + explicit Dropdown(QWidget *parent); + + bool overlaps(const QRect &globalRect); + + QMargins getMargin() const; + +protected: + void paintEvent(QPaintEvent *e) override; + void enterEventHook(QEnterEvent *e) override; + void leaveEventHook(QEvent *e) override; + + bool eventFilter(QObject *obj, QEvent *e) override; + +private: + void startHide(); + void startShow(); + + void otherEnter(); + void otherLeave(); + + void appearanceCallback(); + void hidingFinished(); + void startAnimation(); + + bool _hiding = false; + + QPixmap _cache; + Ui::Animations::Simple _a_appearance; + + base::Timer _hideTimer; + base::Timer _showTimer; + +}; + +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index 9c524e1c7..37d058e8b 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_volume_controller.h" #include "media/audio/media_audio.h" +#include "media/player/media_player_dropdown.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/widgets/continuous_sliders.h" @@ -21,8 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_media_player.h" #include "styles/style_widgets.h" -namespace Media { -namespace Player { +namespace Media::Player { VolumeController::VolumeController( QWidget *parent, @@ -76,166 +76,28 @@ void VolumeController::applyVolumeChange(float64 volume) { } } -VolumeWidget::VolumeWidget( - QWidget *parent, - not_null controller) -: RpWidget(parent) -, _controller(this, controller) -, _hideTimer([=] { startHide(); }) -, _showTimer([=] { startShow(); }) { - hide(); - _controller->setIsVertical(true); +void PrepareVolumeDropdown( + not_null dropdown, + not_null controller) { + const auto volume = Ui::CreateChild( + dropdown.get(), + controller); + volume->show(); + volume->setIsVertical(true); - macWindowDeactivateEvents( - ) | rpl::filter([=] { - return !isHidden(); - }) | rpl::start_with_next([=] { - leaveEvent(nullptr); - }, lifetime()); - - hide(); - auto margin = getMargin(); - resize(margin.left() + st::mediaPlayerVolumeSize.width() + margin.right(), margin.top() + st::mediaPlayerVolumeSize.height() + margin.bottom()); + dropdown->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + const auto rect = QRect(QPoint(), size); + const auto inner = rect.marginsRemoved(dropdown->getMargin()); + volume->setGeometry( + inner.x(), + inner.y() - st::lineWidth, + inner.width(), + (inner.height() + + st::lineWidth + - ((st::mediaPlayerVolumeSize.width() + - st::mediaPlayerPanelPlayback.width) / 2))); + }, volume->lifetime()); } -QMargins VolumeWidget::getMargin() const { - const auto top = st::mediaPlayerHeight - + st::lineWidth - - st::mediaPlayerPlayTop - - st::mediaPlayerVolumeToggle.height; - return QMargins(st::mediaPlayerVolumeMargin, top, st::mediaPlayerVolumeMargin, st::mediaPlayerVolumeMargin); -} - -bool VolumeWidget::overlaps(const QRect &globalRect) { - if (isHidden() || _a_appearance.animating()) return false; - - return rect().marginsRemoved(getMargin()).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); -} - -void VolumeWidget::resizeEvent(QResizeEvent *e) { - auto inner = rect().marginsRemoved(getMargin()); - _controller->setGeometry(inner.x(), inner.y() - st::lineWidth, inner.width(), inner.height() + st::lineWidth - ((st::mediaPlayerVolumeSize.width() - st::mediaPlayerPanelPlayback.width) / 2)); -} - -void VolumeWidget::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_cache.isNull()) { - bool animating = _a_appearance.animating(); - if (animating) { - p.setOpacity(_a_appearance.value(_hiding ? 0. : 1.)); - } else if (_hiding || isHidden()) { - hidingFinished(); - return; - } - p.drawPixmap(0, 0, _cache); - if (!animating) { - showChildren(); - _cache = QPixmap(); - } - return; - } - - // draw shadow - auto shadowedRect = rect().marginsRemoved(getMargin()); - auto shadowedSides = RectPart::Left | RectPart::Right | RectPart::Bottom; - Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides); - auto parts = RectPart::NoTopBottom | RectPart::FullBottom; - Ui::FillRoundRect(p, QRect(shadowedRect.x(), -st::roundRadiusSmall, shadowedRect.width(), shadowedRect.y() + shadowedRect.height() + st::roundRadiusSmall), st::menuBg, Ui::MenuCorners, nullptr, parts); -} - -void VolumeWidget::enterEventHook(QEnterEvent *e) { - _hideTimer.cancel(); - if (_a_appearance.animating()) { - startShow(); - } else { - _showTimer.callOnce(0); - } - return RpWidget::enterEventHook(e); -} - -void VolumeWidget::leaveEventHook(QEvent *e) { - _showTimer.cancel(); - if (_a_appearance.animating()) { - startHide(); - } else { - _hideTimer.callOnce(300); - } - return RpWidget::leaveEventHook(e); -} - -void VolumeWidget::otherEnter() { - _hideTimer.cancel(); - if (_a_appearance.animating()) { - startShow(); - } else { - _showTimer.callOnce(0); - } -} - -void VolumeWidget::otherLeave() { - _showTimer.cancel(); - if (_a_appearance.animating()) { - startHide(); - } else { - _hideTimer.callOnce(0); - } -} - -void VolumeWidget::startShow() { - if (isHidden()) { - show(); - } else if (!_hiding) { - return; - } - _hiding = false; - startAnimation(); -} - -void VolumeWidget::startHide() { - if (_hiding) { - return; - } - - _hiding = true; - startAnimation(); -} - -void VolumeWidget::startAnimation() { - if (_cache.isNull()) { - showChildren(); - _cache = Ui::GrabWidget(this); - } - hideChildren(); - _a_appearance.start( - [=] { appearanceCallback(); }, - _hiding ? 1. : 0., - _hiding ? 0. : 1., - st::defaultInnerDropdown.duration); -} - -void VolumeWidget::appearanceCallback() { - if (!_a_appearance.animating() && _hiding) { - _hiding = false; - hidingFinished(); - } else { - update(); - } -} - -void VolumeWidget::hidingFinished() { - hide(); - _cache = QPixmap(); -} - -bool VolumeWidget::eventFilter(QObject *obj, QEvent *e) { - if (e->type() == QEvent::Enter) { - otherEnter(); - } else if (e->type() == QEvent::Leave) { - otherLeave(); - } - return false; -} - -} // namespace Player -} // namespace Media +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.h b/Telegram/SourceFiles/media/player/media_player_volume_controller.h index bda9d80fe..05a9da23b 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.h +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.h @@ -7,13 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "ui/effects/animations.h" #include "ui/rp_widget.h" #include "base/object_ptr.h" -#include "base/timer.h" namespace Ui { -class IconButton; class MediaSlider; } // namespace Ui @@ -21,8 +18,9 @@ namespace Window { class SessionController; } // namespace Window -namespace Media { -namespace Player { +namespace Media::Player { + +class Dropdown; class VolumeController final : public Ui::RpWidget { public: @@ -43,46 +41,8 @@ private: }; -class VolumeWidget final : public Ui::RpWidget { -public: - VolumeWidget( - QWidget *parent, - not_null controller); +void PrepareVolumeDropdown( + not_null dropdown, + not_null controller); - bool overlaps(const QRect &globalRect); - - QMargins getMargin() const; - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void enterEventHook(QEnterEvent *e) override; - void leaveEventHook(QEvent *e) override; - - bool eventFilter(QObject *obj, QEvent *e) override; - -private: - void startHide(); - void startShow(); - - void otherEnter(); - void otherLeave(); - - void appearanceCallback(); - void hidingFinished(); - void startAnimation(); - - bool _hiding = false; - - QPixmap _cache; - Ui::Animations::Simple _a_appearance; - - object_ptr _controller; - - base::Timer _hideTimer; - base::Timer _showTimer; - -}; - -} // namespace Player -} // namespace Media +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 86c9dce6e..dee206a2b 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -26,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/view/media_view_playback_progress.h" #include "media/player/media_player_button.h" #include "media/player/media_player_instance.h" -#include "media/player/media_player_volume_controller.h" +#include "media/player/media_player_dropdown.h" #include "styles/style_media_player.h" #include "styles/style_media_view.h" #include "history/history_item.h" @@ -239,7 +239,7 @@ Widget::Widget(QWidget *parent, not_null session) , _timeLabel(this, st::mediaPlayerTime) , _playPause(this) , _volumeToggle(this, st::mediaPlayerVolumeToggle) -, _repeatTrack(this, st::mediaPlayerRepeatButton) +, _repeatToggle(this, st::mediaPlayerRepeatButton) , _playbackSpeed(this, st::mediaPlayerSpeedButton) , _close(this, st::mediaPlayerClose) , _shadow(this) @@ -291,7 +291,7 @@ Widget::Widget(QWidget *parent, not_null session) }, lifetime()); updateRepeatTrackIcon(); - _repeatTrack->setClickedCallback([=] { + _repeatToggle->setClickedCallback([=] { instance()->toggleRepeat(AudioMsgId::Type::Song); }); @@ -394,7 +394,7 @@ QPoint Widget::getPositionForVolumeWidget() const { return QPoint(x, height()); } -void Widget::volumeWidgetCreated(VolumeWidget *widget) { +void Widget::volumeWidgetCreated(Dropdown *widget) { _volumeToggle->installEventFilter(widget); } @@ -433,7 +433,7 @@ void Widget::updateControlsGeometry() { if (hasPlaybackSpeedControl()) { _playbackSpeed->moveToRight(right, st::mediaPlayerPlayTop); right += _playbackSpeed->width(); } - _repeatTrack->moveToRight(right, st::mediaPlayerPlayTop); right += _repeatTrack->width(); + _repeatToggle->moveToRight(right, st::mediaPlayerPlayTop); right += _repeatToggle->width(); _volumeToggle->moveToRight(right, st::mediaPlayerPlayTop); right += _volumeToggle->width(); updatePlayPrevNextPositions(); @@ -520,7 +520,7 @@ int Widget::getLabelsLeft() const { int Widget::getLabelsRight() const { auto result = st::mediaPlayerCloseRight + _close->width(); if (_type == AudioMsgId::Type::Song) { - result += _repeatTrack->width() + _volumeToggle->width(); + result += _repeatToggle->width() + _volumeToggle->width(); } if (hasPlaybackSpeedControl()) { result += _playbackSpeed->width(); @@ -543,8 +543,8 @@ void Widget::updateLabelsGeometry() { void Widget::updateRepeatTrackIcon() { auto repeating = instance()->repeatEnabled(AudioMsgId::Type::Song); - _repeatTrack->setIconOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledIcon, repeating ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); - _repeatTrack->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); + _repeatToggle->setIconOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledIcon, repeating ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); + _repeatToggle->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); } void Widget::checkForTypeChange() { @@ -567,7 +567,7 @@ bool Widget::hasPlaybackSpeedControl() const { } void Widget::updateControlsVisibility() { - _repeatTrack->setVisible(_type == AudioMsgId::Type::Song); + _repeatToggle->setVisible(_type == AudioMsgId::Type::Song); _volumeToggle->setVisible(_type == AudioMsgId::Type::Song); _playbackSpeed->setVisible(hasPlaybackSpeedControl()); if (!_shadow->isHidden()) { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index a676af2cb..0373bec7b 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -36,7 +36,7 @@ namespace Player { class PlayButton; class SpeedButton; -class VolumeWidget; +class Dropdown; struct TrackState; class Widget : public Ui::RpWidget, private base::Subscriber { @@ -51,7 +51,7 @@ public: void hideShadow(); QPoint getPositionForVolumeWidget() const; - void volumeWidgetCreated(VolumeWidget *widget); + void volumeWidgetCreated(Dropdown *widget); ~Widget(); @@ -120,7 +120,7 @@ private: object_ptr _playPause; object_ptr _nextTrack = { nullptr }; object_ptr _volumeToggle; - object_ptr _repeatTrack; + object_ptr _repeatToggle; object_ptr _playbackSpeed; object_ptr _close; object_ptr _shadow = { nullptr }; diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 038f8673a..a087769b5 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -80,6 +80,9 @@ PRIVATE media/clip/media_clip_reader.cpp media/clip/media_clip_reader.h + media/player/media_player_dropdown.cpp + media/player/media_player_dropdown.h + passport/ui/passport_details_row.cpp passport/ui/passport_details_row.h passport/ui/passport_form_row.cpp From c7433477bc5dde55c115c0cd70ba805a31e3020a Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 20 Nov 2021 21:51:11 +0400 Subject: [PATCH 018/180] Use ninja as much as possible in docker --- .github/workflows/linux.yml | 3 +- Telegram/build/docker/centos_env/Dockerfile | 68 ++++++++++----------- Telegram/build/docker/centos_env/build.sh | 4 +- snap/snapcraft.yaml | 1 - 4 files changed, 34 insertions(+), 42 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 3b9f053a9..aa4c93bac 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -103,8 +103,7 @@ jobs: -D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF \ $DEFINE - cd ../out/Debug - make -j$(nproc) + cmake --build ../out --config Debug --parallel - name: Check. run: | diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 848406bbd..40e152bb9 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -27,7 +27,7 @@ RUN yum -y install centos-release-scl && yum clean all RUN yum -y install git meson ninja-build autoconf automake libtool \ fontconfig-devel freetype-devel libX11-devel at-spi2-core-devel alsa-lib-devel \ pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel libudev-devel \ - webkitgtk4-devel pkgconfig bison yasm file which xorg-x11-util-macros \ + gtk3-devel pkgconfig bison yasm file which xorg-x11-util-macros \ devtoolset-10-make devtoolset-10-gcc devtoolset-10-gcc-c++ \ devtoolset-10-binutils llvm-toolset-7.0 llvm-toolset-7.0-clang-devel \ llvm-toolset-7.0-llvm-devel perl-XML-Parser && yum clean all @@ -35,7 +35,8 @@ RUN yum -y install git meson ninja-build autoconf automake libtool \ SHELL [ "bash", "-c", ". /opt/rh/devtoolset-10/enable; exec bash -c \"$@\"", "-s"] ENV LibrariesPath /usr/src/Libraries -ENV HFLAGS "-fstack-protector-all -fstack-clash-protection -fPIC -D_FORTIFY_SOURCE=2" +ENV HFLAGS_DEBUG "-fstack-protector-all -fstack-clash-protection -fPIC" +ENV HFLAGS "$HFLAGS_DEBUG -D_FORTIFY_SOURCE=2" WORKDIR $LibrariesPath RUN mkdir /opt/cmake @@ -52,8 +53,8 @@ FROM builder AS extra-cmake-modules RUN git clone -b v5.87.0 --depth=1 $GIT/KDE/extra-cmake-modules.git WORKDIR extra-cmake-modules -RUN cmake -B build . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF -RUN cmake --build build -j$(nproc) +RUN cmake -GNinja -B build . -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF +RUN cmake --build build --parallel RUN DESTDIR="$LibrariesPath/extra-cmake-modules-cache" cmake --install build WORKDIR .. @@ -87,8 +88,8 @@ RUN git clone -b v5.2.5 https://git.tukaani.org/xz.git WORKDIR xz RUN CFLAGS="$HFLAGS" \ - cmake -B build . -DCMAKE_BUILD_TYPE=Release -RUN cmake --build build -j$(nproc) + cmake -GNinja -B build . -DCMAKE_BUILD_TYPE=Release +RUN cmake --build build --parallel RUN DESTDIR="$LibrariesPath/xz-cache" cmake --install build WORKDIR .. @@ -104,7 +105,7 @@ RUN meson build \ --default-library=both \ -Dtests=false -RUN meson compile -C build -j$(nproc) +RUN meson compile -C build RUN DESTDIR="$LibrariesPath/libepoxy-cache" meson install -C build WORKDIR .. @@ -115,13 +116,13 @@ RUN git clone -b 0.4.17 --depth=1 $GIT/libproxy/libproxy.git WORKDIR libproxy RUN git apply ../patches/libproxy.patch -RUN CFLAGS="$HFLAGS" CXXFLAGS="$HFLAGS" cmake -B build . \ +RUN CFLAGS="$HFLAGS" CXXFLAGS="$HFLAGS" cmake -GNinja -B build . \ -DCMAKE_BUILD_TYPE=Release \ -DWITH_DBUS=OFF \ -DWITH_NM=OFF \ -DWITH_NMold=OFF -RUN cmake --build build -j$(nproc) +RUN cmake --build build --parallel RUN DESTDIR="$LibrariesPath/libproxy-cache" cmake --install build WORKDIR .. @@ -131,13 +132,13 @@ FROM builder AS mozjpeg RUN git clone -b v4.0.3 --depth=1 $GIT/mozilla/mozjpeg.git WORKDIR mozjpeg -RUN CFLAGS="$HFLAGS" cmake -B build . \ +RUN CFLAGS="$HFLAGS" cmake -GNinja -B build . \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DWITH_JPEG8=ON \ -DPNG_SUPPORTED=OFF -RUN cmake --build build -j$(nproc) +RUN cmake --build build --parallel RUN DESTDIR="$LibrariesPath/mozjpeg-cache" cmake --install build WORKDIR .. @@ -159,10 +160,10 @@ FROM builder AS rnnoise RUN git clone -b master --depth=1 $GIT/desktop-app/rnnoise WORKDIR rnnoise -RUN CFLAGS="$HFLAGS" cmake -B build . \ +RUN CFLAGS="$HFLAGS" cmake -GNinja -B build . \ -DCMAKE_BUILD_TYPE=Release -RUN cmake --build build -j$(nproc) +RUN cmake --build build --parallel RUN mkdir -p "$LibrariesPath/rnnoise-cache/usr/local/include" RUN cp "include/rnnoise.h" "$LibrariesPath/rnnoise-cache/usr/local/include/" RUN mkdir -p "$LibrariesPath/rnnoise-cache/usr/local/lib" @@ -367,8 +368,8 @@ COPY --from=extra-cmake-modules ${LibrariesPath}/extra-cmake-modules-cache / RUN git clone -b v1.4.0 --depth=1 $GIT/KDE/plasma-wayland-protocols.git WORKDIR plasma-wayland-protocols -RUN cmake -B build . -DCMAKE_BUILD_TYPE=Release -RUN cmake --build build -j$(nproc) +RUN cmake -GNinja -B build . -DCMAKE_BUILD_TYPE=Release +RUN cmake --build build --parallel RUN DESTDIR="$LibrariesPath/plasma-wayland-protocols-cache" cmake --install build WORKDIR .. @@ -396,7 +397,7 @@ RUN meson build \ --buildtype=release \ --default-library=both -RUN meson compile -C build -j$(nproc) +RUN meson compile -C build RUN DESTDIR="$LibrariesPath/drm-cache" meson install -C build WORKDIR .. @@ -437,7 +438,7 @@ RUN meson build \ -Ddocumentation=false \ -Dmoduledir=/usr/lib/vdpau -RUN meson compile -C build -j$(nproc) +RUN meson compile -C build RUN DESTDIR="$LibrariesPath/libvdpau-cache" meson install -C build WORKDIR .. @@ -568,7 +569,7 @@ ADD https://api.github.com/repos/telegramdesktop/openal-soft/git/refs/heads/fix_ RUN git clone -b fix_pulse_default --depth=1 $GIT/telegramdesktop/openal-soft.git WORKDIR openal-soft -RUN CFLAGS="$HFLAGS" CXXFLAGS="$HFLAGS" cmake -B build . \ +RUN CFLAGS="$HFLAGS" CXXFLAGS="$HFLAGS" cmake -GNinja -B build . \ -DCMAKE_BUILD_TYPE=Release \ -DLIBTYPE:STRING=STATIC \ -DALSOFT_EXAMPLES=OFF \ @@ -576,7 +577,7 @@ RUN CFLAGS="$HFLAGS" CXXFLAGS="$HFLAGS" cmake -B build . \ -DALSOFT_UTILS=OFF \ -DALSOFT_CONFIG=OFF -RUN cmake --build build -j$(nproc) +RUN cmake --build build --parallel RUN DESTDIR="$LibrariesPath/openal-cache" cmake --install build WORKDIR .. @@ -616,7 +617,7 @@ RUN meson build \ -Dxkb-config-extra-path=/etc/xkb \ -Dx-locale-root=/usr/share/X11/locale -RUN meson compile -C build -j$(nproc) +RUN meson compile -C build RUN DESTDIR="$LibrariesPath/xkbcommon-cache" meson install -C build WORKDIR .. @@ -759,7 +760,7 @@ RUN [ -z "${QT6}" ] || ./configure -prefix "$QT6_PREFIX" \ -nomake examples \ -nomake tests -RUN [ -z "${QT6}" ] || cmake --build . -j$(nproc) +RUN [ -z "${QT6}" ] || cmake --build . --parallel RUN [ -z "${QT6}" ] || DESTDIR="$LibrariesPath/qt-cache" cmake --install . WORKDIR .. @@ -790,8 +791,8 @@ RUN git clone -b v5.87.0 --depth=1 $GIT/KDE/kwayland.git WORKDIR kwayland RUN [ -z "${QT6}" ] || git apply ../patches/kwayland-qt6.patch -RUN cmake -B build . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF -RUN cmake --build build --target KF5WaylandClient -j$(nproc) +RUN cmake -GNinja -B build . -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=OFF +RUN cmake --build build --target KF5WaylandClient --parallel RUN DESTDIR="$LibrariesPath/kwayland-cache" cmake --install build/src/client WORKDIR .. @@ -838,8 +839,11 @@ RUN meson build WORKDIR ../../.. -RUN CFLAGS="$HFLAGS" CXXFLAGS="$HFLAGS" cmake -B out/Release . \ - -DCMAKE_BUILD_TYPE=Release \ +RUN cmake -G"Ninja Multi-Config" -B out . \ + -DCMAKE_C_FLAGS_RELEASE="-O3 -DNDEBUG $HFLAGS" \ + -DCMAKE_C_FLAGS_DEBUG="-g $HFLAGS_DEBUG" \ + -DCMAKE_CXX_FLAGS_RELEASE="-O3 -DNDEBUG $HFLAGS" \ + -DCMAKE_CXX_FLAGS_DEBUG="-g $HFLAGS_DEBUG" \ -DTG_OWT_BUILD_AUDIO_BACKENDS=OFF \ -DTG_OWT_SPECIAL_TARGET=linux \ -DTG_OWT_LIBJPEG_INCLUDE_PATH=/usr/local/include \ @@ -847,18 +851,8 @@ RUN CFLAGS="$HFLAGS" CXXFLAGS="$HFLAGS" cmake -B out/Release . \ -DTG_OWT_OPUS_INCLUDE_PATH=/usr/local/include/opus \ -DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include -RUN cmake --build out/Release -- -j$(nproc) - -ENV HFLAGS_DEBUG="-fstack-protector-all -fstack-clash-protection -fPIC" -RUN CFLAGS="$HFLAGS_DEBUG" CXXFLAGS="$HFLAGS_DEBUG" cmake -B out/Debug . \ - -DCMAKE_BUILD_TYPE=Debug \ - -DTG_OWT_SPECIAL_TARGET=linux \ - -DTG_OWT_LIBJPEG_INCLUDE_PATH=/usr/local/include \ - -DTG_OWT_OPENSSL_INCLUDE_PATH=$OPENSSL_PREFIX/include \ - -DTG_OWT_OPUS_INCLUDE_PATH=/usr/local/include/opus \ - -DTG_OWT_FFMPEG_INCLUDE_PATH=/usr/local/include - -RUN cmake --build out/Debug -- -j$(nproc) +RUN cmake --build out --config Release --parallel +RUN cmake --build out --config Debug --parallel FROM builder diff --git a/Telegram/build/docker/centos_env/build.sh b/Telegram/build/docker/centos_env/build.sh index 28f000202..8fa1e6ae0 100755 --- a/Telegram/build/docker/centos_env/build.sh +++ b/Telegram/build/docker/centos_env/build.sh @@ -4,7 +4,7 @@ cd Telegram ./configure.sh "$@" if [ -n "$DEBUG" ]; then - cmake --build ../out/Debug -j$(nproc) + cmake --build ../out --config Debug --parallel else - cmake --build ../out/Release -j$(nproc) + cmake --build ../out --config Release --parallel fi diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 6c886298f..f4f0065d8 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -104,7 +104,6 @@ parts: - libopus-dev - libpulse-dev - libssl-dev - - libwebkit2gtk-4.0-dev - libxcb1-dev - libxcb-keysyms1-dev - libxcb-record0-dev From 9308615361c77d983bac458e48196646b0660c3b Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 24 Nov 2021 17:37:12 +0400 Subject: [PATCH 019/180] Fix crash in search in Finder on macOS. --- Telegram/build/prepare/prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 52cab9bbe..77abe924e 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -401,7 +401,7 @@ if customRunCommand: stage('patches', """ git clone https://github.com/desktop-app/patches.git cd patches - git checkout 85bce38ec7 + git checkout eb843853be """) stage('depot_tools', """ From dbf673f67406c892df9564ca4a132b266e161fd6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Nov 2021 10:59:32 +0400 Subject: [PATCH 020/180] Build new tgcalls with C++20 on Windows. --- Telegram/SourceFiles/calls/calls_call.cpp | 6 ++++-- Telegram/ThirdParty/tgcalls | 2 +- Telegram/cmake/lib_tgcalls.cmake | 11 ++++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index d15c24b37..10df62e48 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace tgcalls { class InstanceImpl; +class InstanceV2Impl; class InstanceImplLegacy; class InstanceImplReference; void SetLegacyGlobalServerConfig(const std::string &serverConfig); @@ -50,8 +51,9 @@ constexpr auto kSha256Size = 32; constexpr auto kAuthKeySize = 256; const auto kDefaultVersion = "2.4.4"_q; -const auto RegisterTag = tgcalls::Register(); -const auto RegisterTagLegacy = tgcalls::Register(); +const auto Register = tgcalls::Register(); +const auto RegisterV2 = tgcalls::Register(); +const auto RegisterLegacy = tgcalls::Register(); void AppendEndpoint( std::vector &list, diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index f76a9290f..f340af67a 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit f76a9290fa502a8df473dd872aedf9a553b089cc +Subproject commit f340af67a01c64c3d9e6ba087832717d57cbfb30 diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index 7d94d3d4b..2e93e901c 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -7,7 +7,7 @@ add_library(lib_tgcalls STATIC) if (WIN32) - init_target(lib_tgcalls cxx_std_17) # Small amount of patches required here. + init_target(lib_tgcalls) # Small amount of patches required here. elseif (LINUX) init_target(lib_tgcalls) # All C++20 on Linux, because otherwise ODR violation. else() @@ -62,6 +62,15 @@ PRIVATE VideoCaptureInterfaceImpl.h VideoCapturerInterface.h + v2/InstanceV2Impl.cpp + v2/InstanceV2Impl.h + v2/NativeNetworkingImpl.cpp + v2/NativeNetworkingImpl.h + v2/Signaling.cpp + v2/Signaling.h + v2/SignalingEncryption.cpp + v2/SignalingEncryption.h + # Desktop capturer desktop_capturer/DesktopCaptureSource.h desktop_capturer/DesktopCaptureSource.cpp From dad11a17dd784dcfe7b3f80be087ab2fddd68a06 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Nov 2021 12:20:28 +0400 Subject: [PATCH 021/180] Build tg_owt / tgcalls / tgvoip with C++20. --- Telegram/ThirdParty/tgcalls | 2 +- Telegram/build/prepare/prepare.py | 2 +- Telegram/cmake/lib_tgcalls.cmake | 18 ++---------------- Telegram/cmake/lib_tgvoip.cmake | 9 +-------- 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index f340af67a..b5906f03f 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit f340af67a01c64c3d9e6ba087832717d57cbfb30 +Subproject commit b5906f03f388252d50fc008dcae549f56c2239f4 diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 77abe924e..37c33cc7e 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1154,7 +1154,7 @@ mac: stage('tg_owt', """ git clone https://github.com/desktop-app/tg_owt.git cd tg_owt - git checkout d578c760dc + git checkout ffbdaa616e git submodule init git submodule update src/third_party/libvpx/source/libvpx src/third_party/libyuv win: diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index 2e93e901c..91dbc4ca0 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -5,14 +5,7 @@ # https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL add_library(lib_tgcalls STATIC) - -if (WIN32) - init_target(lib_tgcalls) # Small amount of patches required here. -elseif (LINUX) - init_target(lib_tgcalls) # All C++20 on Linux, because otherwise ODR violation. -else() - init_target(lib_tgcalls cxx_std_14) # Can't use std::optional::value on macOS. -endif() +init_target(lib_tgcalls) # Can't use std::optional::value on macOS. add_library(tdesktop::lib_tgcalls ALIAS lib_tgcalls) @@ -250,14 +243,7 @@ PRIVATE ) add_library(lib_tgcalls_legacy STATIC) - -if (WIN32) - init_target(lib_tgcalls_legacy cxx_std_17) # Small amount of patches required here. -elseif (LINUX) - init_target(lib_tgcalls_legacy) # All C++20 on Linux, because otherwise ODR violation. -else() - init_target(lib_tgcalls_legacy cxx_std_14) # Can't use std::optional::value on macOS. -endif() +init_target(lib_tgcalls_legacy) add_library(tdesktop::lib_tgcalls_legacy ALIAS lib_tgcalls_legacy) diff --git a/Telegram/cmake/lib_tgvoip.cmake b/Telegram/cmake/lib_tgvoip.cmake index f332b1807..68a64ba60 100644 --- a/Telegram/cmake/lib_tgvoip.cmake +++ b/Telegram/cmake/lib_tgvoip.cmake @@ -18,14 +18,7 @@ endif() if (NOT TGVOIP_FOUND) add_library(lib_tgvoip_bundled STATIC) - - if (WIN32) - init_target(lib_tgvoip_bundled cxx_std_17) # Small amount of patches required here. - elseif (LINUX) - init_target(lib_tgvoip_bundled) # All C++20 on Linux, because otherwise ODR violation. - else() - init_target(lib_tgvoip_bundled cxx_std_14) # Can't use std::optional::value on macOS. - endif() + init_target(lib_tgvoip_bundled) option(LIBTGVOIP_DISABLE_ALSA "Disable libtgvoip's ALSA backend (Linux only)." OFF) option(LIBTGVOIP_DISABLE_PULSEAUDIO "Disable libtgvoip's PulseAudio backend (Linux only)." OFF) From efe12e02898d680cd6b04facd749a041353201a4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Nov 2021 13:40:43 +0400 Subject: [PATCH 022/180] Fix tg_owt build on Windows with C++20. --- Telegram/ThirdParty/tgcalls | 2 +- Telegram/build/prepare/prepare.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index b5906f03f..823ec7f0f 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit b5906f03f388252d50fc008dcae549f56c2239f4 +Subproject commit 823ec7f0f59310ade8344ed74efd36d18ebfb018 diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 37c33cc7e..df92142d4 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1154,7 +1154,7 @@ mac: stage('tg_owt', """ git clone https://github.com/desktop-app/tg_owt.git cd tg_owt - git checkout ffbdaa616e + git checkout 667254c20e git submodule init git submodule update src/third_party/libvpx/source/libvpx src/third_party/libyuv win: From 0c695a6a9a9503d09184fc98d6f93c32ca56de3a Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Nov 2021 14:55:24 +0400 Subject: [PATCH 023/180] Update tg_owt with desktop capture fix on macOS. --- Telegram/build/prepare/prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index df92142d4..7a89441fb 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1154,7 +1154,7 @@ mac: stage('tg_owt', """ git clone https://github.com/desktop-app/tg_owt.git cd tg_owt - git checkout 667254c20e + git checkout eaeea95898 git submodule init git submodule update src/third_party/libvpx/source/libvpx src/third_party/libyuv win: From c7678f17acad094857104553a4bfcb514840fe4c Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Nov 2021 18:46:42 +0400 Subject: [PATCH 024/180] Fix build of tg_owt with VS 2022. --- Telegram/build/prepare/prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 7a89441fb..7158f0fd6 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1154,7 +1154,7 @@ mac: stage('tg_owt', """ git clone https://github.com/desktop-app/tg_owt.git cd tg_owt - git checkout eaeea95898 + git checkout b02478677b git submodule init git submodule update src/third_party/libvpx/source/libvpx src/third_party/libyuv win: From bf18907fe60efacebf42a07303bd2056c583ac4a Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 15 Nov 2021 12:08:56 +0400 Subject: [PATCH 025/180] Fix video in calls working on tgcalls v2 implementation. --- Telegram/SourceFiles/calls/calls_call.cpp | 6 ++++-- Telegram/ThirdParty/tgcalls | 2 +- Telegram/cmake/lib_tgcalls.cmake | 4 ---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 10df62e48..a38203ff6 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -38,7 +38,6 @@ namespace tgcalls { class InstanceImpl; class InstanceV2Impl; class InstanceImplLegacy; -class InstanceImplReference; void SetLegacyGlobalServerConfig(const std::string &serverConfig); } // namespace tgcalls @@ -388,12 +387,15 @@ void Call::setupOutgoingVideo() { _videoCaptureIsScreencast); _videoCapture->setOutput(_videoOutgoing->sink()); } + _videoCapture->setState(tgcalls::VideoState::Active); if (_instance) { _instance->setVideoCapture(_videoCapture); } - _videoCapture->setState(tgcalls::VideoState::Active); } else if (_videoCapture) { _videoCapture->setState(tgcalls::VideoState::Inactive); + if (_instance) { + _instance->setVideoCapture(nullptr); + } } }, _lifetime); } diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index 823ec7f0f..0e3f3705e 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit 823ec7f0f59310ade8344ed74efd36d18ebfb018 +Subproject commit 0e3f3705ecce37dc83ede0b04180a8cb0830aec3 diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index 91dbc4ca0..bfb962d6e 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -160,10 +160,6 @@ PRIVATE platform/tdesktop/VideoCameraCapturer.cpp platform/tdesktop/VideoCameraCapturer.h - # All - reference/InstanceImplReference.cpp - reference/InstanceImplReference.h - # third-party third-party/json11.cpp third-party/json11.hpp From 431e3035af28c035161c0b8df9894565f517ccc4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 16:05:11 +0400 Subject: [PATCH 026/180] Update API scheme to layer 135. --- Telegram/Resources/tl/api.tl | 27 ++++++++++++------- Telegram/SourceFiles/api/api_polls.cpp | 3 ++- Telegram/SourceFiles/api/api_sending.cpp | 6 +++-- Telegram/SourceFiles/apiwrap.cpp | 15 +++++++---- .../boxes/peer_list_controllers.cpp | 3 ++- Telegram/SourceFiles/facades.cpp | 11 ++++++++ .../admin_log/history_admin_log_item.cpp | 5 ++++ .../history/history_item_reply_markup.cpp | 14 +++++++--- .../history/history_item_reply_markup.h | 1 + .../SourceFiles/history/history_message.cpp | 3 ++- 10 files changed, 66 insertions(+), 22 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 159b5b1c5..ad07b961d 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -123,13 +123,13 @@ userStatusLastWeek#7bf09fc = UserStatus; userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#29562865 id:long = Chat; -chat#41cbf256 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; +chat#41cbf256 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#6592a1a7 id:long title:string = Chat; -channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; +channel#8261ac61 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat; channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; chatFull#46a6ffb4 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector = ChatFull; -channelFull#59cff963 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector = ChatFull; +channelFull#56662e2e flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer = ChatFull; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; @@ -584,6 +584,8 @@ keyboardButtonBuy#afd93fbb text:string = KeyboardButton; keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton; inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton; keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton; +inputKeyboardButtonUserProfile#e988037b text:string user_id:InputUser = KeyboardButton; +keyboardButtonUserProfile#308660c1 text:string user_id:long = KeyboardButton; keyboardButtonRow#77608b83 buttons:Vector = KeyboardButtonRow; @@ -912,6 +914,7 @@ channelAdminLogEventActionExportedInviteEdit#e90ebb59 prev_invite:ExportedChatIn channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction; channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction; +channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction; channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent; @@ -1287,6 +1290,8 @@ searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosi messages.searchResultsPositions#53b22baf count:int positions:Vector = messages.SearchResultsPositions; +channels.sendAsPeers#8356cda9 peers:Vector chats:Vector users:Vector = channels.SendAsPeers; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1423,9 +1428,9 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; messages.receivedMessages#5a954c0 max_id:int = Vector; messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; -messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int = Updates; -messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int = Updates; -messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer schedule_date:flags.10?int = Updates; +messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; +messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; +messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; messages.report#8953ab4e peer:InputPeer id:Vector reason:ReportReason message:string = Bool; @@ -1468,7 +1473,7 @@ messages.getSavedGifs#5cf09635 hash:long = messages.SavedGifs; messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool; messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults; messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool; -messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates; +messages.sendInlineBotResult#7aa11297 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData; messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.15?int = Updates; messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector = Bool; @@ -1504,7 +1509,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool; messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages; messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory; messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages; -messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector schedule_date:flags.10?int = Updates; +messages.sendMultiMedia#f803138f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile; messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.getSplitRanges#1cff7e08 = Vector; @@ -1560,6 +1565,9 @@ messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector< messages.getSearchResultsCalendar#49f0bde9 peer:InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar; messages.getSearchResultsPositions#6e9583a3 peer:InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions; messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPeer user_id:InputUser = Updates; +messages.hideAllChatJoinRequests#e085f4ea flags:# approved:flags.0?true peer:InputPeer link:flags.1?string = Updates; +messages.toggleNoForwards#b11eafa2 peer:InputPeer enabled:Bool = Updates; +messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1640,6 +1648,7 @@ channels.getInactiveChannels#11e831ee = messages.InactiveChats; channels.convertToGigagroup#b290c69 channel:InputChannel = Updates; channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool; channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages; +channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; @@ -1707,4 +1716,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats; -// LAYER 134 +// LAYER 135 diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp index fcfe7f983..65d00b58f 100644 --- a/Telegram/SourceFiles/api/api_polls.cpp +++ b/Telegram/SourceFiles/api/api_polls.cpp @@ -73,7 +73,8 @@ void Polls::create( MTP_long(base::RandomValue()), MTPReplyMarkup(), MTPVector(), - MTP_int(action.options.scheduled) + MTP_int(action.options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=]( const MTPUpdates &result, const MTP::Response &response) mutable { diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 2da7235b9..4afdc695a 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -142,7 +142,8 @@ void SendExistingMedia( MTP_long(randomId), MTPReplyMarkup(), sentEntities, - MTP_int(message.action.options.scheduled) + MTP_int(message.action.options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=](const MTPUpdates &result) { api->applyUpdates(result, randomId); finish(); @@ -297,7 +298,8 @@ bool SendDice(MessageToSend &message) { MTP_long(randomId), MTPReplyMarkup(), MTP_vector(), - MTP_int(message.action.options.scheduled) + MTP_int(message.action.options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=](const MTPUpdates &result) { api->applyUpdates(result, randomId); finish(); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 49d5eb682..a33c18a77 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3707,7 +3707,8 @@ void ApiWrap::forwardMessages( MTP_vector(ids), MTP_vector(randomIds), peer->input, - MTP_int(action.options.scheduled) + MTP_int(action.options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=](const MTPUpdates &result) { applyUpdates(result); if (shared && !--shared->requestsLeft) { @@ -4109,7 +4110,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) { MTP_long(randomId), MTPReplyMarkup(), sentEntities, - MTP_int(action.options.scheduled) + MTP_int(action.options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=]( const MTPUpdates &result, const MTP::Response &response) { @@ -4236,7 +4238,8 @@ void ApiWrap::sendInlineResult( MTP_long(randomId), MTP_long(data->getQueryId()), MTP_string(data->getId()), - MTP_int(action.options.scheduled) + MTP_int(action.options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=]( const MTPUpdates &result, const MTP::Response &response) { @@ -4389,7 +4392,8 @@ void ApiWrap::sendMediaWithRandomId( MTP_long(randomId), MTPReplyMarkup(), sentEntities, - MTP_int(options.scheduled) + MTP_int(options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=](const MTPUpdates &result) { applyUpdates(result); finish(); @@ -4491,7 +4495,8 @@ void ApiWrap::sendAlbumIfReady(not_null album) { peer->input, MTP_int(replyTo), MTP_vector(medias), - MTP_int(album->options.scheduled) + MTP_int(album->options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=](const MTPUpdates &result) { _sendingAlbums.remove(groupId); applyUpdates(result); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 112d5fe5a..455c88543 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -50,7 +50,8 @@ void ShareBotGame(not_null bot, not_null chat) { MTP_long(randomId), MTPReplyMarkup(), MTPVector(), - MTP_int(0) // schedule_date + MTP_int(0), // schedule_date + MTPInputPeer() // #TODO send_as )).done([=](const MTPUpdates &result) { api->applyUpdates(result, randomId); finish(); diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index bbf301720..4f12da3bd 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -210,6 +210,17 @@ void activateBotCommand( case ButtonType::Auth: UrlAuthBox::Activate(msg, row, column); break; + + case ButtonType::UserProfile: { + const auto session = &msg->history()->session(); + const auto userId = UserId(button->data.toULongLong()); + if (const auto user = session->data().userLoaded(userId)) { + const auto &windows = session->windows(); + if (!windows.empty()) { + windows.front()->showPeerInfo(user); + } + } + } break; } } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index dbb43c341..ebb5bfc5b 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -1113,7 +1113,10 @@ void GenerateItems( textcmdLink(adminIndex, user->name)), data.vinvite(), user->createOpenLink()); + }; + auto createToggleNoForwards = [&](const MTPDchannelAdminLogEventActionToggleNoForwards &data) { + // #TODO noforwards }; action.match([&](const MTPDchannelAdminLogEventActionChangeTitle &data) { @@ -1182,6 +1185,8 @@ void GenerateItems( createChangeHistoryTTL(data); }, [&](const MTPDchannelAdminLogEventActionParticipantJoinByRequest &data) { createParticipantJoinByRequest(data); + }, [&](const MTPDchannelAdminLogEventActionToggleNoForwards &data) { + createToggleNoForwards(data); }); } diff --git a/Telegram/SourceFiles/history/history_item_reply_markup.cpp b/Telegram/SourceFiles/history/history_item_reply_markup.cpp index 9950b2696..230538d92 100644 --- a/Telegram/SourceFiles/history/history_item_reply_markup.cpp +++ b/Telegram/SourceFiles/history/history_item_reply_markup.cpp @@ -95,9 +95,6 @@ void HistoryMessageMarkupData::fillRows( qba(data.vurl()), qs(data.vfwd_text().value_or_empty()), data.vbutton_id().v); - }, [&](const MTPDinputKeyboardButtonUrlAuth &data) { - LOG(("API Error: inputKeyboardButtonUrlAuth received.")); - // Should not get those for the users. }, [&](const MTPDkeyboardButtonRequestPoll &data) { const auto quiz = [&] { if (!data.vquiz()) { @@ -113,6 +110,17 @@ void HistoryMessageMarkupData::fillRows( Type::RequestPoll, qs(data.vtext()), quiz); + }, [&](const MTPDkeyboardButtonUserProfile &data) { + row.emplace_back( + Type::UserProfile, + qs(data.vtext()), + QByteArray::number(data.vuser_id().v)); + }, [&](const MTPDinputKeyboardButtonUrlAuth &data) { + LOG(("API Error: inputKeyboardButtonUrlAuth.")); + // Should not get those for the users. + }, [&](const MTPDinputKeyboardButtonUserProfile &data) { + LOG(("API Error: inputKeyboardButtonUserProfile.")); + // Should not get those for the users. }); } if (!row.empty()) { diff --git a/Telegram/SourceFiles/history/history_item_reply_markup.h b/Telegram/SourceFiles/history/history_item_reply_markup.h index 8418bc50f..c857b5953 100644 --- a/Telegram/SourceFiles/history/history_item_reply_markup.h +++ b/Telegram/SourceFiles/history/history_item_reply_markup.h @@ -40,6 +40,7 @@ struct HistoryMessageMarkupButton { Game, Buy, Auth, + UserProfile, }; HistoryMessageMarkupButton( diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 7ef0b990f..062222c00 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -311,7 +311,8 @@ void FastShareMessage(not_null item) { MTP_vector(msgIds), MTP_vector(generateRandom()), peer->input, - MTP_int(options.scheduled) + MTP_int(options.scheduled), + MTPInputPeer() // #TODO send_as )).done([=](const MTPUpdates &updates, mtpRequestId requestId) { history->session().api().applyUpdates(updates); data->requests.remove(requestId); From 9be47f0870ad06a2d35e1b50da1c7bdd510b85e3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 18:03:17 +0400 Subject: [PATCH 027/180] Allow restricting forwards in groups / channels. --- Telegram/Resources/langs/lang.strings | 3 + Telegram/SourceFiles/apiwrap.cpp | 5 ++ .../boxes/peers/edit_peer_info_box.cpp | 59 ++++++++++++++++++- Telegram/SourceFiles/data/data_channel.cpp | 4 ++ Telegram/SourceFiles/data/data_channel.h | 2 + Telegram/SourceFiles/data/data_chat.cpp | 12 ++-- Telegram/SourceFiles/data/data_chat.h | 2 + Telegram/SourceFiles/data/data_peer.cpp | 11 ++++ Telegram/SourceFiles/data/data_peer.h | 1 + Telegram/SourceFiles/data/data_session.cpp | 15 ++--- .../SourceFiles/history/history_message.cpp | 4 +- 11 files changed, 105 insertions(+), 13 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 3e4be3074..898d13bd9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -148,6 +148,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group."; "lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel."; "lng_error_post_link_invalid" = "Unfortunately, you can't access this message. You are not a member of the chat where it was posted."; +"lng_error_noforwards_group" = "Sorry, forwarding is disabled from this group."; +"lng_error_noforwards_channel" = "Sorry, forwarding is disabled from this channel."; "lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?"; "lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?"; "lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?"; @@ -1808,6 +1810,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_contact_title" = "Edit contact name"; "lng_edit_channel_title" = "Edit channel"; "lng_edit_sign_messages" = "Sign messages"; +"lng_edit_allow_forwards" = "Allow saving content"; "lng_edit_group" = "Edit group"; "lng_edit_self_title" = "Edit your name"; "lng_confirm_contact_data" = "New Contact"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index a33c18a77..b8b6bfca9 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -504,6 +504,11 @@ void ApiWrap::sendMessageFail( scheduled.removeSending(item); Ui::show(Box(tr::lng_cant_do_this(tr::now))); } + } else if (error.type() == qstr("CHAT_FORWARDS_RESTRICTED")) { + Ui::ShowMultilineToast({ .text = { peer->isBroadcast() + ? tr::lng_error_noforwards_channel(tr::now) + : tr::lng_error_noforwards_group(tr::now) + }, .duration = kJoinErrorDuration }); } if (const auto item = _session->data().message(itemId)) { Assert(randomId != 0); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 807b31a54..e7c32c8ba 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -277,6 +277,7 @@ private: std::optional description; std::optional hiddenPreHistory; std::optional signatures; + std::optional forwards; std::optional linkedChat; }; @@ -296,6 +297,7 @@ private: void fillLinkedChatButton(); //void fillInviteLinkButton(); void fillSignaturesButton(); + void fillForwardsButton(); void fillHistoryVisibilityButton(); void fillManageSection(); void fillPendingRequestsButton(); @@ -312,6 +314,7 @@ private: bool validateDescription(Saving &to) const; bool validateHistoryVisibility(Saving &to) const; bool validateSignatures(Saving &to) const; + bool validateForwards(Saving &to) const; void save(); void saveUsername(); @@ -320,6 +323,7 @@ private: void saveDescription(); void saveHistoryVisibility(); void saveSignatures(); + void saveForwards(); void savePhoto(); void pushSaveStage(FnMut &&lambda); void continueSave(); @@ -341,6 +345,7 @@ private: std::optional _historyVisibilitySavedValue; std::optional _usernameSavedValue; std::optional _signaturesSavedValue; + std::optional _forwardsSavedValue; const not_null _navigation; const not_null _box; @@ -795,6 +800,21 @@ void Controller::fillSignaturesButton() { }, _controls.buttonsLayout->lifetime()); } +void Controller::fillForwardsButton() { + Expects(_controls.buttonsLayout != nullptr); + + AddButtonWithText( + _controls.buttonsLayout, + tr::lng_edit_allow_forwards(), + rpl::single(QString()), + [=] {} + )->toggleOn(rpl::single(_peer->allowsForwarding()) + )->toggledValue( + ) | rpl::start_with_next([=](bool toggled) { + _forwardsSavedValue = toggled; + }, _controls.buttonsLayout->lifetime()); +} + void Controller::fillHistoryVisibilityButton() { Expects(_controls.buttonsLayout != nullptr); @@ -864,6 +884,9 @@ void Controller::fillManageSection() { ? (channel->canEditSignatures() && !channel->isMegagroup()) : false; }(); + const auto canEditForwards = [&] { + return isChannel ? channel->amCreator() : chat->amCreator(); + }(); const auto canEditPreHistoryHidden = [&] { return isChannel ? channel->canEditPreHistoryHidden() @@ -937,8 +960,12 @@ void Controller::fillManageSection() { if (canEditSignatures) { fillSignaturesButton(); } + if (canEditForwards) { + fillForwardsButton(); + } if (canEditPreHistoryHidden || canEditSignatures + || canEditForwards //|| canEditInviteLinks || canViewOrEditLinkedChat || canEditUsername) { @@ -1154,7 +1181,8 @@ std::optional Controller::validate() const { && validateTitle(result) && validateDescription(result) && validateHistoryVisibility(result) - && validateSignatures(result)) { + && validateSignatures(result) + && validateForwards(result)) { return result; } return {}; @@ -1229,6 +1257,14 @@ bool Controller::validateSignatures(Saving &to) const { return true; } +bool Controller::validateForwards(Saving &to) const { + if (!_forwardsSavedValue.has_value()) { + return true; + } + to.forwards = _forwardsSavedValue; + return true; +} + void Controller::save() { Expects(_wrap != nullptr); @@ -1243,6 +1279,7 @@ void Controller::save() { pushSaveStage([=] { saveDescription(); }); pushSaveStage([=] { saveHistoryVisibility(); }); pushSaveStage([=] { saveSignatures(); }); + pushSaveStage([=] { saveForwards(); }); pushSaveStage([=] { savePhoto(); }); continueSave(); } @@ -1499,6 +1536,26 @@ void Controller::saveSignatures() { }).send(); } +void Controller::saveForwards() { + if (!_savingData.forwards + || *_savingData.forwards == _peer->allowsForwarding()) { + return continueSave(); + } + _api.request(MTPmessages_ToggleNoForwards( + _peer->input, + MTP_bool(!*_savingData.forwards) + )).done([=](const MTPUpdates &result) { + _peer->session().api().applyUpdates(result); + continueSave(); + }).fail([=](const MTP::Error &error) { + if (error.type() == qstr("CHAT_NOT_MODIFIED")) { + continueSave(); + } else { + cancelSave(); + } + }).send(); +} + void Controller::savePhoto() { auto image = _controls.photo ? _controls.photo->takeResultImage() diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index e16fd4447..532918314 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -470,6 +470,10 @@ bool ChannelData::canWrite() const { && !amRestricted(Restriction::SendMessages))); } +bool ChannelData::allowsForwarding() const { + return !(flags() & Flag::NoForwards); +} + bool ChannelData::canViewMembers() const { return flags() & Flag::CanViewParticipants; } diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 420746b26..94bf7e645 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -50,6 +50,7 @@ enum class ChannelDataFlag { CanViewParticipants = (1 << 17), HasLink = (1 << 18), SlowmodeEnabled = (1 << 19), + NoForwards = (1 << 20), }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; using ChannelDataFlags = base::flags; @@ -293,6 +294,7 @@ public: // Like in ChatData. [[nodiscard]] bool canWrite() const; + [[nodiscard]] bool allowsForwarding() const; [[nodiscard]] bool canEditInformation() const; [[nodiscard]] bool canEditPermissions() const; [[nodiscard]] bool canEditUsername() const; diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 217cac924..59f1525d3 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -28,7 +28,7 @@ ChatData::ChatData(not_null owner, PeerId id) , inputChat(MTP_long(peerToChat(id).bare)) { _flags.changes( ) | rpl::start_with_next([=](const Flags::Change &change) { - if (change.diff & ChatDataFlag::CallNotEmpty) { + if (change.diff & Flag::CallNotEmpty) { if (const auto history = this->owner().historyLoaded(this)) { history->updateChatListEntry(); } @@ -63,6 +63,10 @@ bool ChatData::canWrite() const { return amIn() && !amRestricted(Restriction::SendMessages); } +bool ChatData::allowsForwarding() const { + return !(flags() & Flag::NoForwards); +} + bool ChatData::canEditInformation() const { return amIn() && !amRestricted(Restriction::ChangeInfo); } @@ -74,7 +78,7 @@ bool ChatData::canEditPermissions() const { bool ChatData::canEditUsername() const { return amCreator() - && (flags() & ChatDataFlag::CanSetUsername); + && (flags() & Flag::CanSetUsername); } bool ChatData::canEditPreHistoryHidden() const { @@ -222,7 +226,7 @@ void ChatData::setGroupCall( scheduleDate); owner().registerGroupCall(_call.get()); session().changes().peerUpdated(this, UpdateFlag::GroupCall); - addFlags(ChatDataFlag::CallActive); + addFlags(Flag::CallActive); }); } @@ -236,7 +240,7 @@ void ChatData::clearGroupCall() { _call = nullptr; } session().changes().peerUpdated(this, UpdateFlag::GroupCall); - removeFlags(ChatDataFlag::CallActive | ChatDataFlag::CallNotEmpty); + removeFlags(Flag::CallActive | Flag::CallNotEmpty); } void ChatData::setGroupCallDefaultJoinAs(PeerId peerId) { diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index 97f022c82..3f8f4ab05 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -18,6 +18,7 @@ enum class ChatDataFlag { CallActive = (1 << 5), CallNotEmpty = (1 << 6), CanSetUsername = (1 << 7), + NoForwards = (1 << 8), }; inline constexpr bool is_flag_type(ChatDataFlag) { return true; }; using ChatDataFlags = base::flags; @@ -109,6 +110,7 @@ public: // Like in ChannelData. [[nodiscard]] bool canWrite() const; + [[nodiscard]] bool allowsForwarding() const; [[nodiscard]] bool canEditInformation() const; [[nodiscard]] bool canEditPermissions() const; [[nodiscard]] bool canEditUsername() const; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 74aec4c17..19ea59404 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -848,6 +848,17 @@ bool PeerData::canWrite() const { return false; } +bool PeerData::allowsForwarding() const { + if (const auto user = asUser()) { + return true; + } else if (const auto channel = asChannel()) { + return channel->allowsForwarding(); + } else if (const auto chat = asChat()) { + return chat->allowsForwarding(); + } + return false; +} + Data::RestrictionCheckResult PeerData::amRestricted( ChatRestriction right) const { using Result = Data::RestrictionCheckResult; diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index dd9376214..800ddf9d1 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -274,6 +274,7 @@ public: } [[nodiscard]] bool canWrite() const; + [[nodiscard]] bool allowsForwarding() const; [[nodiscard]] Data::RestrictionCheckResult amRestricted( ChatRestriction right) const; [[nodiscard]] bool amAnonymous() const; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index d3d13dda5..ed4ccbd47 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -641,7 +641,8 @@ not_null Session::processChat(const MTPChat &data) { | Flag::Deactivated | Flag::Forbidden | Flag::CallActive - | Flag::CallNotEmpty; + | Flag::CallNotEmpty + | Flag::NoForwards; const auto flagsSet = (data.is_left() ? Flag::Left : Flag()) | (data.is_kicked() ? Flag::Kicked : Flag()) | (data.is_creator() ? Flag::Creator : Flag()) @@ -651,7 +652,8 @@ not_null Session::processChat(const MTPChat &data) { || (chat->groupCall() && chat->groupCall()->fullCount() > 0)) ? Flag::CallNotEmpty - : Flag()); + : Flag()) + | (data.is_noforwards() ? Flag::NoForwards : Flag()); chat->setFlags((chat->flags() & ~flagsMask) | flagsSet); chat->count = data.vparticipants_count().v; @@ -739,10 +741,8 @@ not_null Session::processChat(const MTPChat &data) { | Flag::CallActive | Flag::CallNotEmpty | Flag::Forbidden - | (!minimal - ? Flag::Left - | Flag::Creator - : Flag()); + | (!minimal ? (Flag::Left | Flag::Creator) : Flag()) + | Flag::NoForwards; const auto flagsSet = (data.is_broadcast() ? Flag::Broadcast : Flag()) | (data.is_verified() ? Flag::Verified : Flag()) | (data.is_scam() ? Flag::Scam : Flag()) @@ -762,7 +762,8 @@ not_null Session::processChat(const MTPChat &data) { | (!minimal ? (data.is_left() ? Flag::Left : Flag()) | (data.is_creator() ? Flag::Creator : Flag()) - : Flag()); + : Flag()) + | (data.is_noforwards() ? Flag::NoForwards : Flag()); channel->setFlags((channel->flags() & ~flagsMask) | flagsSet); channel->setName( diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 062222c00..ddde57fce 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1038,7 +1038,9 @@ void HistoryMessage::applySentMessage( } bool HistoryMessage::allowsForward() const { - return isRegular() && (!_media || _media->allowsForward()); + return isRegular() + && history()->peer->allowsForwarding() + && (!_media || _media->allowsForward()); } bool HistoryMessage::allowsSendNow() const { From 154c0e6bb251206a872f84606e003ff9b942eeaa Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 18:23:39 +0400 Subject: [PATCH 028/180] Don't show empty top bar selections. --- .../view/history_view_top_bar_widget.cpp | 54 ++++++++++++------- .../view/history_view_top_bar_widget.h | 1 + 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 909b1947c..ea30062bb 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -266,11 +266,11 @@ void TopBarWidget::setChooseForReportReason( updateControlsVisibility(); updateControlsGeometry(); update(); - if (wasNoReason != nowNoReason && _selectedCount > 0) { + if (wasNoReason != nowNoReason && showSelectedState()) { toggleSelectedControls(false); finishAnimating(); } - setCursor((nowNoReason && !_selectedCount) + setCursor((nowNoReason && !showSelectedState()) ? style::cur_pointer : style::cur_default); } @@ -584,9 +584,9 @@ QRect TopBarWidget::getMembersShowAreaGeometry() const { } void TopBarWidget::mousePressEvent(QMouseEvent *e) { - auto handleClick = (e->button() == Qt::LeftButton) + const auto handleClick = (e->button() == Qt::LeftButton) && (e->pos().y() < st::topBarHeight) - && !_selectedCount + && !showSelectedState() && !_chooseForReportReason; if (handleClick) { if (_animatingMode && _back->rect().contains(e->pos())) { @@ -924,7 +924,7 @@ void TopBarWidget::updateControlsVisibility() { void TopBarWidget::updateMembersShowArea() { const auto membersShowAreaNeeded = [&] { const auto peer = _activeChat.key.peer(); - if ((_selectedCount > 0) || !peer) { + if (showSelectedState() || !peer) { return false; } else if (const auto chat = peer->asChat()) { return chat->amIn(); @@ -949,44 +949,58 @@ void TopBarWidget::updateMembersShowArea() { _membersShowArea->setGeometry(getMembersShowAreaGeometry()); } +bool TopBarWidget::showSelectedState() const { + return (_selectedCount > 0) + && (_canDelete || _canForward || _canSendNow); +} + void TopBarWidget::showSelected(SelectedState state) { auto canDelete = (state.count > 0 && state.count == state.canDeleteCount); auto canForward = (state.count > 0 && state.count == state.canForwardCount); auto canSendNow = (state.count > 0 && state.count == state.canSendNowCount); - if (_selectedCount == state.count && _canDelete == canDelete && _canForward == canForward && _canSendNow == canSendNow) { + auto count = (!canDelete && !canForward && !canSendNow) ? 0 : state.count; + if (_selectedCount == count + && _canDelete == canDelete + && _canForward == canForward + && _canSendNow == canSendNow) { return; } - if (state.count == 0) { + if (count == 0) { // Don't change the visible buttons if the selection is cancelled. canDelete = _canDelete; canForward = _canForward; canSendNow = _canSendNow; } - auto wasSelected = (_selectedCount > 0); - _selectedCount = state.count; - if (_selectedCount > 0) { + const auto wasSelectedState = showSelectedState(); + const auto visibilityChanged = (_canDelete != canDelete) + || (_canForward != canForward) + || (_canSendNow != canSendNow); + _selectedCount = count; + _canDelete = canDelete; + _canForward = canForward; + _canSendNow = canSendNow; + const auto nowSelectedState = showSelectedState(); + if (nowSelectedState) { _forward->setNumbersText(_selectedCount); _sendNow->setNumbersText(_selectedCount); _delete->setNumbersText(_selectedCount); - if (!wasSelected) { + if (!wasSelectedState) { _forward->finishNumbersAnimation(); _sendNow->finishNumbersAnimation(); _delete->finishNumbersAnimation(); } } - auto hasSelected = (_selectedCount > 0); - if (_canDelete != canDelete || _canForward != canForward || _canSendNow != canSendNow) { - _canDelete = canDelete; - _canForward = canForward; - _canSendNow = canSendNow; + if (visibilityChanged) { updateControlsVisibility(); } - if (wasSelected != hasSelected && !_chooseForReportReason) { - setCursor(hasSelected ? style::cur_default : style::cur_pointer); + if (wasSelectedState != nowSelectedState && !_chooseForReportReason) { + setCursor(nowSelectedState + ? style::cur_default + : style::cur_pointer); updateMembersShowArea(); - toggleSelectedControls(hasSelected); + toggleSelectedControls(nowSelectedState); } else { updateControlsGeometry(); } @@ -1002,7 +1016,7 @@ void TopBarWidget::toggleSelectedControls(bool shown) { } bool TopBarWidget::showSelectedActions() const { - return (_selectedCount > 0) && !_chooseForReportReason; + return showSelectedState() && !_chooseForReportReason; } void TopBarWidget::selectedShowCallback() { diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h index ae4a2b382..fae3f8522 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -57,6 +57,7 @@ public: void updateControlsVisibility(); void finishAnimating(); void showSelected(SelectedState state); + [[nodiscard]] bool showSelectedState() const; rpl::producer membersShowAreaActive() const { return _membersShowAreaActive.events(); } From 3a536c6f7595cdbc010da8118b6a77189d44c8ba Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 18:50:50 +0400 Subject: [PATCH 029/180] Show forwards toggle in recent actions. --- Telegram/Resources/langs/lang.strings | 2 ++ .../history/admin_log/history_admin_log_item.cpp | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 898d13bd9..2462ecb6b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2476,6 +2476,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_invites_disabled" = "{from} disabled group invites"; "lng_admin_log_signatures_enabled" = "{from} enabled signatures"; "lng_admin_log_signatures_disabled" = "{from} disabled signatures"; +"lng_admin_log_forwards_enabled" = "{from} allowed content copying"; +"lng_admin_log_forwards_disabled" = "{from} restricted content copying"; "lng_admin_log_history_made_hidden" = "{from} made group history hidden for new members"; "lng_admin_log_history_made_visible" = "{from} made group history visible for new members"; "lng_admin_log_pinned_message" = "{from} pinned message:"; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index ebb5bfc5b..7e3d8b69a 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -1116,7 +1116,11 @@ void GenerateItems( }; auto createToggleNoForwards = [&](const MTPDchannelAdminLogEventActionToggleNoForwards &data) { - // #TODO noforwards + const auto disabled = (data.vnew_value().type() == mtpc_boolTrue); + const auto text = (disabled + ? tr::lng_admin_log_forwards_disabled + : tr::lng_admin_log_forwards_enabled); + addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; action.match([&](const MTPDchannelAdminLogEventActionChangeTitle &data) { From 487e8a9009155a379cc2d7366fd86ea2e0f3ef24 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 20:13:59 +0400 Subject: [PATCH 030/180] Disable copy to clipboard if noforwards. --- Telegram/Resources/langs/lang.strings | 6 ++- .../history/history_inner_widget.cpp | 51 +++++++++++++------ .../history/history_inner_widget.h | 1 + .../view/history_view_context_menu.cpp | 46 ++++++++++------- .../history/view/history_view_list_widget.cpp | 39 ++++++++++++-- .../history/view/history_view_list_widget.h | 24 ++++++--- .../view/history_view_pinned_section.cpp | 4 ++ .../view/history_view_pinned_section.h | 1 + .../view/history_view_replies_section.cpp | 4 ++ .../view/history_view_replies_section.h | 1 + .../view/history_view_scheduled_section.cpp | 4 ++ .../view/history_view_scheduled_section.h | 1 + .../media/view/media_view_overlay_widget.cpp | 27 +++++++++- .../media/view/media_view_overlay_widget.h | 3 ++ 14 files changed, 166 insertions(+), 46 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2462ecb6b..0569a664c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -148,8 +148,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group."; "lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel."; "lng_error_post_link_invalid" = "Unfortunately, you can't access this message. You are not a member of the chat where it was posted."; -"lng_error_noforwards_group" = "Sorry, forwarding is disabled from this group."; -"lng_error_noforwards_channel" = "Sorry, forwarding is disabled from this channel."; +"lng_error_noforwards_group" = "Sorry, forwarding from this group is disabled by admins."; +"lng_error_noforwards_channel" = "Sorry, forwarding from this channel is disabled by admins."; +"lng_error_nocopy_group" = "Sorry, copying from this group is disabled by admins."; +"lng_error_nocopy_channel" = "Sorry, copying from this channel is disabled by admins."; "lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?"; "lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?"; "lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?"; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 0d1cf828a..7cbaec14f 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -25,7 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/chat_style.h" #include "ui/widgets/popup_menu.h" #include "ui/image/image.h" -#include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "ui/effects/path_shift_gradient.h" #include "ui/text/text_options.h" #include "ui/boxes/report_box.h" @@ -1494,7 +1494,8 @@ void HistoryInner::mouseActionFinish( if (QGuiApplication::clipboard()->supportsSelection() && !_selected.empty() - && _selected.cbegin()->second != FullSelection) { + && _selected.cbegin()->second != FullSelection + && _peer->allowsForwarding()) { const auto [item, selection] = *_selected.cbegin(); if (const auto view = item->mainView()) { TextUtilities::SetClipboardText( @@ -1673,7 +1674,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }; const auto addPhotoActions = [&](not_null photo) { const auto media = photo->activeMediaView(); - if (!photo->isNull() && media && media->loaded()) { + if (!photo->isNull() && media && media->loaded() && _peer->allowsForwarding()) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { savePhotoToFile(photo); })); @@ -1753,7 +1754,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (lnkPhoto || lnkDocument) { const auto item = _dragStateItem; const auto itemId = item ? item->fullId() : FullMsgId(); - if (isUponSelected > 0) { + if (isUponSelected > 0 && _peer->allowsForwarding()) { _menu->addAction( (isUponSelected > 1 ? tr::lng_context_copy_selected_items(tr::now) @@ -1844,11 +1845,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto view = item ? item->mainView() : nullptr; if (isUponSelected > 0) { - _menu->addAction( - ((isUponSelected > 1) - ? tr::lng_context_copy_selected_items(tr::now) - : tr::lng_context_copy_selected(tr::now)), - [=] { copySelectedText(); }); + if (_peer->allowsForwarding()) { + _menu->addAction( + ((isUponSelected > 1) + ? tr::lng_context_copy_selected_items(tr::now) + : tr::lng_context_copy_selected(tr::now)), + [=] { copySelectedText(); }); + } addItemActions(item, item); } else { addItemActions(item, albumPartItem); @@ -1892,6 +1895,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (!item->isService() && view && !link + && _peer->allowsForwarding() && (view->hasVisibleText() || mediaHasTextForCopy)) { _menu->addAction(tr::lng_context_copy_text(tr::now), [=] { copyContextText(itemId); @@ -1997,8 +2001,22 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } +bool HistoryInner::showCopyRestriction() { + if (_peer->allowsForwarding()) { + return false; + } + Ui::ShowMultilineToast({ + .text = { _peer->isBroadcast() + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + void HistoryInner::copySelectedText() { - TextUtilities::SetClipboardText(getSelectedText()); + if (!showCopyRestriction()) { + TextUtilities::SetClipboardText(getSelectedText()); + } } void HistoryInner::savePhotoToFile(not_null photo) { @@ -2027,10 +2045,10 @@ void HistoryInner::copyContextImage(not_null photo) { const auto media = photo->activeMediaView(); if (photo->isNull() || !media || !media->loaded()) { return; + } else if (!showCopyRestriction()) { + const auto image = media->image(Data::PhotoSize::Large)->original(); + QGuiApplication::clipboard()->setImage(image); } - - const auto image = media->image(Data::PhotoSize::Large)->original(); - QGuiApplication::clipboard()->setImage(image); } void HistoryInner::showStickerPackInfo(not_null document) { @@ -2078,7 +2096,9 @@ void HistoryInner::saveContextGif(FullMsgId itemId) { } void HistoryInner::copyContextText(FullMsgId itemId) { - if (const auto item = session().data().message(itemId)) { + if (showCopyRestriction()) { + return; + } else if (const auto item = session().data().message(itemId)) { if (const auto group = session().data().groups().find(item)) { TextUtilities::SetClipboardText(HistoryGroupText(group)); } else { @@ -2174,7 +2194,8 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) { copySelectedText(); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E - && e->modifiers().testFlag(Qt::ControlModifier)) { + && e->modifiers().testFlag(Qt::ControlModifier) + && !showCopyRestriction()) { TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index d55f5308e..6252a3b0f 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -342,6 +342,7 @@ private: void blockSenderItem(FullMsgId itemId); void blockSenderAsGroup(FullMsgId itemId); void copySelectedText(); + bool showCopyRestriction(); // Does any of the shown histories has this flag set. bool hasPendingResizedItems() const; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index daadcbe0b..217d47944 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -129,15 +129,19 @@ void AddPhotoActions( not_null menu, not_null photo, not_null list) { - menu->addAction( - tr::lng_context_save_image(tr::now), - App::LambdaDelayed( - st::defaultDropdownMenu.menu.ripple.hideDuration, - &photo->session(), - [=] { SavePhotoToFile(photo); })); - menu->addAction(tr::lng_context_copy_image(tr::now), [=] { - CopyImage(photo); - }); + if (!list->hasCopyRestriction()) { + menu->addAction( + tr::lng_context_save_image(tr::now), + App::LambdaDelayed( + st::defaultDropdownMenu.menu.ripple.hideDuration, + &photo->session(), + [=] { SavePhotoToFile(photo); })); + menu->addAction(tr::lng_context_copy_image(tr::now), [=] { + if (!list->showCopyRestriction()) { + CopyImage(photo); + } + }); + } if (photo->hasAttachedStickers()) { const auto controller = list->controller(); auto callback = [=] { @@ -903,12 +907,14 @@ base::unique_qptr FillContextMenu( const auto hasSelection = !request.selectedItems.empty() || !request.selectedText.empty(); - if (request.overSelection) { + if (request.overSelection && !list->hasCopyRestriction()) { const auto text = request.selectedItems.empty() ? tr::lng_context_copy_selected(tr::now) : tr::lng_context_copy_selected_items(tr::now); result->addAction(text, [=] { - TextUtilities::SetClipboardText(list->getSelectedText()); + if (!list->showCopyRestriction()) { + TextUtilities::SetClipboardText(list->getSelectedText()); + } }); } @@ -930,17 +936,21 @@ base::unique_qptr FillContextMenu( view->data()->fullId(), list); } - if (!link && (view->hasVisibleText() || mediaHasTextForCopy)) { + if (!link + && (view->hasVisibleText() || mediaHasTextForCopy) + && !list->hasCopyRestriction()) { const auto asGroup = (request.pointState != PointState::GroupPart); result->addAction(tr::lng_context_copy_text(tr::now), [=] { - if (const auto item = owner->message(itemId)) { - if (asGroup) { - if (const auto group = owner->groups().find(item)) { - TextUtilities::SetClipboardText(HistoryGroupText(group)); - return; + if (!list->showCopyRestriction()) { + if (const auto item = owner->message(itemId)) { + if (asGroup) { + if (const auto group = owner->groups().find(item)) { + TextUtilities::SetClipboardText(HistoryGroupText(group)); + return; + } } + TextUtilities::SetClipboardText(HistoryItemText(item)); } - TextUtilities::SetClipboardText(HistoryItemText(item)); } }); } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 43d615f77..74f2766bf 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "ui/widgets/popup_menu.h" #include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "ui/inactive_press.h" #include "ui/effects/path_shift_gradient.h" #include "ui/chat/chat_theme.h" @@ -1164,6 +1165,25 @@ bool ListWidget::isEmpty() const { && (_itemsHeight + _itemsRevealHeight == 0); } + + +bool ListWidget::hasCopyRestriction() const { + return _delegate->listCopyRestrictionType() != CopyRestrictionType::None; +} + +bool ListWidget::showCopyRestriction() { + const auto type = _delegate->listCopyRestrictionType(); + if (type == CopyRestrictionType::None) { + return false; + } + Ui::ShowMultilineToast({ + .text = { (type == CopyRestrictionType::Channel) + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + int ListWidget::itemMinimalHeight() const { return st::msgMarginTopAttached + st::msgPhotoSize @@ -1892,11 +1912,13 @@ void ListWidget::keyPressEvent(QKeyEvent *e) { _delegate->listCancelRequest(); } } else if (e == QKeySequence::Copy - && (hasSelectedText() || hasSelectedItems())) { + && (hasSelectedText() || hasSelectedItems()) + && !showCopyRestriction()) { TextUtilities::SetClipboardText(getSelectedText()); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E - && e->modifiers().testFlag(Qt::ControlModifier)) { + && e->modifiers().testFlag(Qt::ControlModifier) + && !showCopyRestriction()) { TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { @@ -2417,7 +2439,8 @@ void ListWidget::mouseActionFinish( if (QGuiApplication::clipboard()->supportsSelection() && _selectedTextItem - && _selectedTextRange.from != _selectedTextRange.to) { + && _selectedTextRange.from != _selectedTextRange.to + && !hasCopyRestriction()) { if (const auto view = viewForItem(_selectedTextItem)) { TextUtilities::SetClipboardText( view->selectedText(_selectedTextRange), @@ -3058,4 +3081,14 @@ void ConfirmSendNowSelectedItems(not_null widget) { clearSelection); } + +CopyRestrictionType CopyRestrictionTypeFor( + not_null peer) { + return peer->allowsForwarding() + ? CopyRestrictionType::None + : peer->isBroadcast() + ? CopyRestrictionType::Channel + : CopyRestrictionType::Group; +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 7d52f1f06..b0aa959cf 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -41,6 +41,12 @@ enum class CursorState : char; enum class PointState : char; enum class Context : char; +enum class CopyRestrictionType : char { + None, + Group, + Channel, +}; + struct SelectedItem { explicit SelectedItem(FullMsgId msgId) : msgId(msgId) { } @@ -94,6 +100,7 @@ public: const FullMsgId &context) = 0; virtual void listHandleViaClick(not_null bot) = 0; virtual not_null listChatTheme() = 0; + virtual CopyRestrictionType listCopyRestrictionType() = 0; }; @@ -101,7 +108,6 @@ struct SelectionData { bool canDelete = false; bool canForward = false; bool canSendNow = false; - }; using SelectedMap = base::flat_map< @@ -199,11 +205,14 @@ public: void selectItem(not_null item); void selectItemAsGroup(not_null item); - bool loadedAtTopKnown() const; - bool loadedAtTop() const; - bool loadedAtBottomKnown() const; - bool loadedAtBottom() const; - bool isEmpty() const; + [[nodiscard]] bool loadedAtTopKnown() const; + [[nodiscard]] bool loadedAtTop() const; + [[nodiscard]] bool loadedAtBottomKnown() const; + [[nodiscard]] bool loadedAtBottom() const; + [[nodiscard]] bool isEmpty() const; + + [[nodiscard]] bool hasCopyRestriction() const; + [[nodiscard]] bool showCopyRestriction(); // AbstractTooltipShower interface QString tooltipText() const override; @@ -605,4 +614,7 @@ void ConfirmDeleteSelectedItems(not_null widget); void ConfirmForwardSelectedItems(not_null widget); void ConfirmSendNowSelectedItems(not_null widget); +[[nodiscard]] CopyRestrictionType CopyRestrictionTypeFor( + not_null peer); + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index db237777b..8bae69101 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -676,6 +676,10 @@ not_null PinnedWidget::listChatTheme() { return _theme.get(); } +CopyRestrictionType PinnedWidget::listCopyRestrictionType() { + return CopyRestrictionTypeFor(_history->peer); +} + void PinnedWidget::confirmDeleteSelected() { ConfirmDeleteSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.h b/Telegram/SourceFiles/history/view/history_view_pinned_section.h index 27c81678b..562df6734 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -103,6 +103,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; + CopyRestrictionType listCopyRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index a54941487..0131c24c5 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1927,6 +1927,10 @@ not_null RepliesWidget::listChatTheme() { return _theme.get(); } +CopyRestrictionType RepliesWidget::listCopyRestrictionType() { + return CopyRestrictionTypeFor(_history->peer); +} + void RepliesWidget::confirmDeleteSelected() { ConfirmDeleteSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 14d6847f5..072141ac7 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -138,6 +138,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; + CopyRestrictionType listCopyRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 48ac4f7ca..e08733df8 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1231,6 +1231,10 @@ not_null ScheduledWidget::listChatTheme() { return _theme.get(); } +CopyRestrictionType ScheduledWidget::listCopyRestrictionType() { + return CopyRestrictionType::None; +} + void ScheduledWidget::confirmSendNowSelected() { ConfirmSendNowSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 6bdd38cf4..982b738c6 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -119,6 +119,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; + CopyRestrictionType listCopyRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 6618b4cc8..8b1763fc8 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/platform/ui_platform_utility.h" #include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "ui/text/format_values.h" #include "ui/item_text_options.h" #include "ui/ui_utility.h" @@ -554,6 +555,23 @@ QSize OverlayWidget::flipSizeByRotation(QSize size) const { return FlipSizeByRotation(size, _rotation); } +bool OverlayWidget::hasCopyRestriction() const { + return _history && !_history->peer->allowsForwarding(); +} + +bool OverlayWidget::showCopyRestriction() { + if (!hasCopyRestriction()) { + return false; + } + Ui::ShowMultilineToast({ + .parentOverride = _widget, + .text = { _history->peer->isBroadcast() + ? tr::lng_error_nocopy_channel(tr::now) + : tr::lng_error_nocopy_group(tr::now) }, + }); + return true; +} + bool OverlayWidget::videoShown() const { return _streamed && !_streamed->instance.info().video.cover.isNull(); } @@ -891,8 +909,10 @@ void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) { : tr::lng_context_show_in_folder(tr::now); addAction(text, [=] { showInFolder(); }); } - if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) { - addAction(tr::lng_mediaview_copy(tr::now), [=] { copyMedia(); }); + if (!hasCopyRestriction()) { + if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) { + addAction(tr::lng_mediaview_copy(tr::now), [=] { copyMedia(); }); + } } if ((_photo && _photo->hasAttachedStickers()) || (_document && _document->hasAttachedStickers())) { @@ -1756,6 +1776,9 @@ void OverlayWidget::showMediaOverview() { } void OverlayWidget::copyMedia() { + if (showCopyRestriction()) { + return; + } _dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); if (_document) { QGuiApplication::clipboard()->setImage(transformedShownContent()); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index dbd63bdf9..d6d779ebc 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -386,6 +386,9 @@ private: void validatePhotoImage(Image *image, bool blurred); void validatePhotoCurrentImage(); + [[nodiscard]] bool hasCopyRestriction() const; + [[nodiscard]] bool showCopyRestriction(); + [[nodiscard]] QSize flipSizeByRotation(QSize size) const; void applyVideoSize(); From fd6751233e6e9e0e0a8c9bc101bdb87865185492 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 21:37:01 +0400 Subject: [PATCH 031/180] Disable selecting items with actions. --- .../history/history_inner_widget.cpp | 148 +++++++++++------- .../history/history_inner_widget.h | 7 +- .../view/history_view_context_menu.cpp | 5 +- .../history/view/history_view_list_widget.cpp | 45 ++++-- .../history/view/history_view_list_widget.h | 4 + .../view/history_view_pinned_section.cpp | 4 + .../view/history_view_pinned_section.h | 1 + .../view/history_view_replies_section.cpp | 4 + .../view/history_view_replies_section.h | 1 + .../view/history_view_scheduled_section.cpp | 4 + .../view/history_view_scheduled_section.h | 1 + 11 files changed, 157 insertions(+), 67 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 7cbaec14f..447c41429 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -66,6 +66,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_poll.h" #include "data/data_photo.h" #include "data/data_photo_media.h" +#include "data/data_peer_values.h" +#include "data/data_chat.h" #include "data/data_user.h" #include "data/data_file_click_handler.h" #include "data/data_file_origin.h" @@ -258,12 +260,61 @@ HistoryInner::HistoryInner( ) | rpl::start_with_next([=](int d) { _scroll->scrollToY(_scroll->scrollTop() + d); }, _scroll->lifetime()); + + setupSharingDisallowed(); } Main::Session &HistoryInner::session() const { return _controller->session(); } +void HistoryInner::setupSharingDisallowed() { + Expects(_peer != nullptr); + + if (_peer->isUser()) { + _sharingDisallowed = false; + return; + } + const auto chat = _peer->asChat(); + const auto channel = _peer->asChannel(); + _sharingDisallowed = chat + ? Data::PeerFlagValue(chat, ChatDataFlag::NoForwards) + : Data::PeerFlagValue(channel, ChannelDataFlag::NoForwards); + + auto rights = chat + ? chat->adminRightsValue() + : channel->adminRightsValue(); + auto canDelete = std::move( + rights + ) | rpl::map([=] { + return chat + ? chat->canDeleteMessages() + : channel->canDeleteMessages(); + }); + rpl::combine( + _sharingDisallowed.value(), + std::move(canDelete) + ) | rpl::filter([=](bool disallowed, bool canDelete) { + return hasSelectRestriction() && !getSelectedItems().empty(); + }) | rpl::start_with_next([=] { + _widget->clearSelected(); + if (_mouseAction == MouseAction::PrepareSelect) { + mouseActionCancel(); + } + }, lifetime()); +} + +bool HistoryInner::hasSelectRestriction() const { + if (!_sharingDisallowed.current()) { + return false; + } else if (const auto chat = _peer->asChat()) { + return !chat->canDeleteMessages(); + } else if (const auto channel = _peer->asChannel()) { + return !channel->canDeleteMessages(); + } + return true; +} + void HistoryInner::messagesReceived( PeerData *peer, const QVector &messages) { @@ -1216,12 +1267,12 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but _selected.emplace(_mouseActionItem, selStatus); _mouseAction = MouseAction::Selecting; repaintItem(_mouseActionItem); - } else { + } else if (!hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; } } } - } else if (!_pressWasInactive) { + } else if (!_pressWasInactive && !hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; // start items select } } @@ -1250,7 +1301,8 @@ std::unique_ptr HistoryInner::prepareDrag() { } const auto pressedHandler = ClickHandler::getPressed(); - if (dynamic_cast(pressedHandler.get())) { + if (dynamic_cast(pressedHandler.get()) + || !_peer->allowsForwarding()) { return nullptr; } @@ -1281,7 +1333,6 @@ std::unique_ptr HistoryInner::prepareDrag() { } } } - auto urls = QList(); const auto selectedText = [&] { if (uponSelected) { @@ -1737,6 +1788,29 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } }; + const auto addSelectMessageAction = [&]( + not_null item, + bool asGroup = true) { + if (item->isRegular() + && !item->isService() + && !hasSelectRestriction()) { + const auto itemId = item->fullId(); + _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { + if (const auto item = session->data().message(itemId)) { + if (const auto view = item->mainView()) { + if (asGroup) { + changeSelectionAsGroup(&_selected, item, SelectAction::Select); + } else { + changeSelection(&_selected, item, SelectAction::Select); + } + repaintItem(item); + _widget->updateTopBarSelection(); + } + } + }); + } + }; + if (hasWhoReadItem) { const auto participantChosen = [=](uint64 id) { controller->showPeerInfo(PeerId(id)); @@ -1808,17 +1882,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }); } } - if (item->isRegular() && !item->isService()) { - _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { - if (const auto item = session->data().message(itemId)) { - if (const auto view = item->mainView()) { - changeSelection(&_selected, item, SelectAction::Select); - repaintItem(item); - _widget->updateTopBarSelection(); - } - } - }); - } + addSelectMessageAction(item, false); if (isUponSelected != -2 && blockSender) { _menu->addAction(tr::lng_profile_block_user(tr::now), [=] { blockSenderItem(itemId); @@ -1959,37 +2023,14 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }); } } - if (item->isRegular() && !item->isService()) { - _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { - if (const auto item = session->data().message(itemId)) { - if (const auto view = item->mainView()) { - changeSelectionAsGroup(&_selected, item, SelectAction::Select); - repaintItem(view); - _widget->updateTopBarSelection(); - } - } - }); - } + addSelectMessageAction(item); if (isUponSelected != -2 && canBlockSender) { _menu->addAction(tr::lng_profile_block_user(tr::now), [=] { blockSenderAsGroup(itemId); }); } - } else { - if (App::mousedItem() - && App::mousedItem()->data()->isRegular() - && !App::mousedItem()->data()->isService()) { - const auto itemId = App::mousedItem()->data()->fullId(); - _menu->addAction(tr::lng_context_select_msg(tr::now), [=] { - if (const auto item = session->data().message(itemId)) { - if (const auto view = item->mainView()) { - changeSelectionAsGroup(&_selected, item, SelectAction::Select); - repaintItem(item); - _widget->updateTopBarSelection(); - } - } - }); - } + } else if (App::mousedItem()) { + addSelectMessageAction(App::mousedItem()->data()); } } @@ -2001,8 +2042,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } +bool HistoryInner::hasCopyRestriction() const { + return !_peer->allowsForwarding(); +} + bool HistoryInner::showCopyRestriction() { - if (_peer->allowsForwarding()) { + if (!hasCopyRestriction()) { return false; } Ui::ShowMultilineToast({ @@ -2817,18 +2862,6 @@ MessageIdsList HistoryInner::getSelectedItems() const { return result; } -void HistoryInner::selectItem(not_null item) { - if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { - _selected.clear(); - } else if (_selected.size() == MaxSelectedItems - && _selected.find(item) == _selected.cend()) { - return; - } - _selected.emplace(item, FullSelection); - _widget->updateTopBarSelection(); - _widget->update(); -} - void HistoryInner::onTouchSelect() { _touchSelect = true; mouseActionStart(_touchPos, Qt::LeftButton); @@ -3115,6 +3148,9 @@ void HistoryInner::mouseActionUpdate() { void HistoryInner::updateDragSelection(Element *dragSelFrom, Element *dragSelTo, bool dragSelecting) { if (_dragSelFrom == dragSelFrom && _dragSelTo == dragSelTo && _dragSelecting == dragSelecting) { return; + } else if (dragSelFrom && hasSelectRestriction()) { + updateDragSelection(nullptr, nullptr, false); + return; } _dragSelFrom = dragSelFrom; _dragSelTo = dragSelTo; @@ -3248,7 +3284,9 @@ void HistoryInner::notifyMigrateUpdated() { } void HistoryInner::applyDragSelection() { - applyDragSelection(&_selected); + if (!hasSelectRestriction()) { + applyDragSelection(&_selected); + } } bool HistoryInner::isSelected( diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 6252a3b0f..c7e28ca60 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -85,7 +85,6 @@ public: HistoryView::TopBarWidget::SelectedState getSelectionState() const; void clearSelected(bool onlyTextSelection = false); MessageIdsList getSelectedItems() const; - void selectItem(not_null item); bool inSelectionMode() const; bool elementIntersectsRange( not_null view, @@ -342,7 +341,11 @@ private: void blockSenderItem(FullMsgId itemId); void blockSenderAsGroup(FullMsgId itemId); void copySelectedText(); + + void setupSharingDisallowed(); + [[nodiscard]] bool hasCopyRestriction() const; bool showCopyRestriction(); + [[nodiscard]] bool hasSelectRestriction() const; // Does any of the shown histories has this flag set. bool hasPendingResizedItems() const; @@ -416,6 +419,8 @@ private: Ui::SelectScrollManager _selectScroll; + rpl::variable _sharingDisallowed = false; + Ui::TouchScrollState _touchScrollState = Ui::TouchScrollState::Manual; bool _touchPrevPosValid = false; bool _touchWaitingAcceleration = false; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 217d47944..ce8b19d4c 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -811,7 +811,10 @@ bool AddSelectMessageAction( const auto item = request.item; if (request.overSelection && !request.selectedItems.empty()) { return false; - } else if (!item || item->isLocal() || item->isService()) { + } else if (!item + || item->isLocal() + || item->isService() + || list->hasSelectRestriction()) { return false; } const auto owner = &item->history()->owner(); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 74f2766bf..7ea987b29 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1076,7 +1076,9 @@ void ListWidget::cancelSelection() { } void ListWidget::selectItem(not_null item) { - if (const auto view = viewForItem(item)) { + if (hasSelectRestriction()) { + return; + } else if (const auto view = viewForItem(item)) { clearTextSelection(); changeSelection( _selected, @@ -1087,7 +1089,9 @@ void ListWidget::selectItem(not_null item) { } void ListWidget::selectItemAsGroup(not_null item) { - if (const auto view = viewForItem(item)) { + if (hasSelectRestriction()) { + return; + } else if (const auto view = viewForItem(item)) { clearTextSelection(); changeSelectionAsGroup( _selected, @@ -1165,8 +1169,6 @@ bool ListWidget::isEmpty() const { && (_itemsHeight + _itemsRevealHeight == 0); } - - bool ListWidget::hasCopyRestriction() const { return _delegate->listCopyRestrictionType() != CopyRestrictionType::None; } @@ -1184,6 +1186,11 @@ bool ListWidget::showCopyRestriction() { return true; } +bool ListWidget::hasSelectRestriction() const { + return _delegate->listSelectRestrictionType() + != CopyRestrictionType::None; +} + int ListWidget::itemMinimalHeight() const { return st::msgMarginTopAttached + st::msgPhotoSize @@ -1751,7 +1758,9 @@ void ListWidget::paintEvent(QPaintEvent *e) { } void ListWidget::applyDragSelection() { - applyDragSelection(_selected); + if (!hasSelectRestriction()) { + applyDragSelection(_selected); + } clearDragSelection(); pushSelectedItems(); } @@ -2080,7 +2089,9 @@ void ListWidget::leaveEventHook(QEvent *e) { } void ListWidget::updateDragSelection() { - if (!_overState.itemId || !_pressState.itemId) { + if (!_overState.itemId + || !_pressState.itemId + || hasSelectRestriction()) { clearDragSelection(); return; } else if (_items.empty() || !_overElement || !_selectEnabled) { @@ -2281,7 +2292,7 @@ void ListWidget::mouseActionStart( } else if (hasSelectedItems()) { if (overSelectedItems()) { _mouseAction = MouseAction::PrepareDrag; - } else if (!_pressWasInactive) { + } else if (!_pressWasInactive && !hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; } } @@ -2326,7 +2337,7 @@ void ListWidget::mouseActionStart( _mouseTextSymbol, _mouseTextSymbol)); _mouseAction = MouseAction::Selecting; - } else { + } else if (!hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; } } @@ -2653,7 +2664,8 @@ std::unique_ptr ListWidget::prepareDrag() { return nullptr; } auto pressedHandler = ClickHandler::getPressed(); - if (dynamic_cast(pressedHandler.get())) { + if (dynamic_cast(pressedHandler.get()) + || hasCopyRestriction()) { return nullptr; } @@ -3081,7 +3093,6 @@ void ConfirmSendNowSelectedItems(not_null widget) { clearSelection); } - CopyRestrictionType CopyRestrictionTypeFor( not_null peer) { return peer->allowsForwarding() @@ -3091,4 +3102,18 @@ CopyRestrictionType CopyRestrictionTypeFor( : CopyRestrictionType::Group; } +CopyRestrictionType SelectRestrictionTypeFor( + not_null peer) { + if (const auto chat = peer->asChat()) { + return chat->canDeleteMessages() + ? CopyRestrictionType::None + : CopyRestrictionTypeFor(peer); + } else if (const auto channel = peer->asChannel()) { + return channel->canDeleteMessages() + ? CopyRestrictionType::None + : CopyRestrictionTypeFor(peer); + } + return CopyRestrictionType::None; +} + } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index b0aa959cf..2ced1f8b9 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -101,6 +101,7 @@ public: virtual void listHandleViaClick(not_null bot) = 0; virtual not_null listChatTheme() = 0; virtual CopyRestrictionType listCopyRestrictionType() = 0; + virtual CopyRestrictionType listSelectRestrictionType() = 0; }; @@ -213,6 +214,7 @@ public: [[nodiscard]] bool hasCopyRestriction() const; [[nodiscard]] bool showCopyRestriction(); + [[nodiscard]] bool hasSelectRestriction() const; // AbstractTooltipShower interface QString tooltipText() const override; @@ -616,5 +618,7 @@ void ConfirmSendNowSelectedItems(not_null widget); [[nodiscard]] CopyRestrictionType CopyRestrictionTypeFor( not_null peer); +[[nodiscard]] CopyRestrictionType SelectRestrictionTypeFor( + not_null peer); } // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index 8bae69101..cd07eafad 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -680,6 +680,10 @@ CopyRestrictionType PinnedWidget::listCopyRestrictionType() { return CopyRestrictionTypeFor(_history->peer); } +CopyRestrictionType PinnedWidget::listSelectRestrictionType() { + return SelectRestrictionTypeFor(_history->peer); +} + void PinnedWidget::confirmDeleteSelected() { ConfirmDeleteSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.h b/Telegram/SourceFiles/history/view/history_view_pinned_section.h index 562df6734..e36afffee 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -104,6 +104,7 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listSelectRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 0131c24c5..7071d6f24 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1931,6 +1931,10 @@ CopyRestrictionType RepliesWidget::listCopyRestrictionType() { return CopyRestrictionTypeFor(_history->peer); } +CopyRestrictionType RepliesWidget::listSelectRestrictionType() { + return SelectRestrictionTypeFor(_history->peer); +} + void RepliesWidget::confirmDeleteSelected() { ConfirmDeleteSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 072141ac7..c7a83473e 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -139,6 +139,7 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listSelectRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index e08733df8..1cc373f0e 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1235,6 +1235,10 @@ CopyRestrictionType ScheduledWidget::listCopyRestrictionType() { return CopyRestrictionType::None; } +CopyRestrictionType ScheduledWidget::listSelectRestrictionType() { + return CopyRestrictionType::None; +} + void ScheduledWidget::confirmSendNowSelected() { ConfirmSendNowSelectedItems(_inner); } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 982b738c6..47a805c40 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -120,6 +120,7 @@ public: void listHandleViaClick(not_null bot) override; not_null listChatTheme() override; CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listSelectRestrictionType() override; protected: void resizeEvent(QResizeEvent *e) override; From 96c910190b5a737678f2f1610f877b82389860ab Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 21:49:23 +0400 Subject: [PATCH 032/180] Restrict saving files as if noforwards. --- .../history/history_inner_widget.cpp | 16 +++++++++------ .../view/history_view_context_menu.cpp | 8 ++++++-- .../info/media/info_media_list_widget.cpp | 20 ++++++++++--------- .../media/view/media_view_overlay_widget.cpp | 8 ++++++-- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 447c41429..ed3725bc0 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1776,9 +1776,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { showContextInFolder(document); }); } - _menu->addAction(lnkIsVideo ? tr::lng_context_save_video(tr::now) : (lnkIsVoice ? tr::lng_context_save_audio(tr::now) : (lnkIsAudio ? tr::lng_context_save_audio_file(tr::now) : tr::lng_context_save_file(tr::now))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { - saveDocumentToFile(itemId, document); - })); + if (_peer->allowsForwarding()) { + _menu->addAction(lnkIsVideo ? tr::lng_context_save_video(tr::now) : (lnkIsVoice ? tr::lng_context_save_audio(tr::now) : (lnkIsAudio ? tr::lng_context_save_audio_file(tr::now) : tr::lng_context_save_file(tr::now))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { + saveDocumentToFile(itemId, document); + })); + } if (document->hasAttachedStickers()) { _menu->addAction(tr::lng_context_attached_stickers(tr::now), [=] { session->api().attachedStickers().requestAttachedStickerSets( @@ -1932,9 +1934,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { Api::ToggleFavedSticker(document, itemId); }); } - _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { - saveDocumentToFile(itemId, document); - })); + if (_peer->allowsForwarding()) { + _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { + saveDocumentToFile(itemId, document); + })); + } } } if (const auto media = item->media()) { diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index ce8b19d4c..9bc30dd53 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -188,7 +188,11 @@ void ShowInFolder(not_null document) { void AddSaveDocumentAction( not_null menu, Data::FileOrigin origin, - not_null document) { + not_null document, + not_null list) { + if (list->hasCopyRestriction()) { + return; + } const auto save = [=] { DocumentSaveClickHandler::Save( origin, @@ -272,7 +276,7 @@ void AddDocumentActions( tr::lng_context_attached_stickers(tr::now), std::move(callback)); } - AddSaveDocumentAction(menu, contextId, document); + AddSaveDocumentAction(menu, contextId, document, list); } void AddPostLinkAction( diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 0a72d810d..5584b182f 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -1595,15 +1595,17 @@ void ListWidget::showContextMenu( document, DocumentSaveClickHandler::Mode::ToNewFile); }); - _contextMenu->addAction( - (isVideo - ? tr::lng_context_save_video(tr::now) - : isVoice - ? tr::lng_context_save_audio(tr::now) - : isAudio - ? tr::lng_context_save_audio_file(tr::now) - : tr::lng_context_save_file(tr::now)), - std::move(handler)); + if (_peer->allowsForwarding()) { + _contextMenu->addAction( + (isVideo + ? tr::lng_context_save_video(tr::now) + : isVoice + ? tr::lng_context_save_audio(tr::now) + : isAudio + ? tr::lng_context_save_audio_file(tr::now) + : tr::lng_context_save_file(tr::now)), + std::move(handler)); + } } } } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 8b1763fc8..6d4e8df8f 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -727,7 +727,9 @@ void OverlayWidget::refreshNavVisibility() { } bool OverlayWidget::contentCanBeSaved() const { - if (_photo) { + if (hasCopyRestriction()) { + return false; + } else if (_photo) { return _photo->hasVideo() || _photoMedia->loaded(); } else if (_document) { return _document->filepath(true).isEmpty() && !_document->loading(); @@ -943,7 +945,9 @@ void OverlayWidget::fillContextMenuActions(const MenuCallback &addAction) { if (canDelete) { addAction(tr::lng_mediaview_delete(tr::now), [=] { deleteMedia(); }); } - addAction(tr::lng_mediaview_save_as(tr::now), [=] { saveAs(); }); + if (!hasCopyRestriction()) { + addAction(tr::lng_mediaview_save_as(tr::now), [=] { saveAs(); }); + } if (const auto overviewType = computeOverviewType()) { const auto text = _document From c849d17667dd18d858d183d4fbbb25cc6c8f8301 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Nov 2021 22:32:26 +0400 Subject: [PATCH 033/180] Remove corner download if noforwards. --- Telegram/SourceFiles/data/data_document.cpp | 6 ++-- Telegram/SourceFiles/data/data_document.h | 2 +- .../SourceFiles/data/data_document_media.cpp | 4 +-- .../SourceFiles/data/data_document_media.h | 2 +- .../data/data_document_resolver.cpp | 2 +- .../SourceFiles/data/data_shared_media.cpp | 2 +- .../history/history_inner_widget.cpp | 28 +++++++++++-------- .../view/history_view_context_menu.cpp | 2 +- .../view/media/history_view_document.cpp | 11 ++++---- .../history/view/media/history_view_gif.cpp | 15 +++++----- .../inline_bots/inline_bot_layout_item.cpp | 2 +- .../media/view/media_view_overlay_widget.cpp | 10 +++---- .../SourceFiles/overview/overview_layout.cpp | 23 +++++++-------- 13 files changed, 59 insertions(+), 50 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index e3983403d..c7849e800 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -1158,12 +1158,14 @@ bool DocumentData::useStreamingLoader() const { || isVoiceMessage(); } -bool DocumentData::canBeStreamed() const { +bool DocumentData::canBeStreamed(HistoryItem *item) const { // Streaming couldn't be used with external player // Maybe someone brave will implement this once upon a time... return hasRemoteLocation() && supportsStreaming() - && (!cUseExternalVideoPlayer() || !isVideoFile()); + && (!isVideoFile() + || !cUseExternalVideoPlayer() + || (item && !item->history()->peer->allowsForwarding())); } void DocumentData::setInappPlaybackFailed() { diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index f2eeb688f..1b8c3da61 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -233,7 +233,7 @@ public: [[nodiscard]] Storage::Cache::Key cacheKey() const; [[nodiscard]] uint8 cacheTag() const; - [[nodiscard]] bool canBeStreamed() const; + [[nodiscard]] bool canBeStreamed(HistoryItem *item) const; [[nodiscard]] auto createStreamingLoader( Data::FileOrigin origin, bool forceRemoteLoader) const diff --git a/Telegram/SourceFiles/data/data_document_media.cpp b/Telegram/SourceFiles/data/data_document_media.cpp index 8e5c63fec..47481cdbb 100644 --- a/Telegram/SourceFiles/data/data_document_media.cpp +++ b/Telegram/SourceFiles/data/data_document_media.cpp @@ -348,10 +348,10 @@ float64 DocumentMedia::progress() const { : (loaded() ? 1. : 0.); } -bool DocumentMedia::canBePlayed() const { +bool DocumentMedia::canBePlayed(HistoryItem *item) const { return !_owner->inappPlaybackFailed() && _owner->useStreamingLoader() - && (loaded() || _owner->canBeStreamed()); + && (loaded() || _owner->canBeStreamed(item)); } bool DocumentMedia::thumbnailEnoughForSticker() const { diff --git a/Telegram/SourceFiles/data/data_document_media.h b/Telegram/SourceFiles/data/data_document_media.h index 9329023c9..5a2fea7c7 100644 --- a/Telegram/SourceFiles/data/data_document_media.h +++ b/Telegram/SourceFiles/data/data_document_media.h @@ -74,7 +74,7 @@ public: [[nodiscard]] QByteArray bytes() const; [[nodiscard]] bool loaded(bool check = false) const; [[nodiscard]] float64 progress() const; - [[nodiscard]] bool canBePlayed() const; + [[nodiscard]] bool canBePlayed(HistoryItem *item) const; void automaticLoad(Data::FileOrigin origin, const HistoryItem *item); diff --git a/Telegram/SourceFiles/data/data_document_resolver.cpp b/Telegram/SourceFiles/data/data_document_resolver.cpp index 7c2335734..8a8e9cab1 100644 --- a/Telegram/SourceFiles/data/data_document_resolver.cpp +++ b/Telegram/SourceFiles/data/data_document_resolver.cpp @@ -252,7 +252,7 @@ void ResolveDocument( if (document->isTheme() && media->loaded(true)) { showDocument(); location.accessDisable(); - } else if (media->canBePlayed()) { + } else if (media->canBePlayed(item)) { if (document->isAudioFile() || document->isVoiceMessage() || document->isVideoMessage()) { diff --git a/Telegram/SourceFiles/data/data_shared_media.cpp b/Telegram/SourceFiles/data/data_shared_media.cpp index e6afc073f..6f584ecbb 100644 --- a/Telegram/SourceFiles/data/data_shared_media.cpp +++ b/Telegram/SourceFiles/data/data_shared_media.cpp @@ -74,7 +74,7 @@ bool IsItemGoodForType(const not_null item, Type type) { || ((videoType || photoVideoType) && videoDoc) || (fileType && (document->isTheme() || document->isImage() - || !document->canBeStreamed())); + || !document->canBeStreamed(item))); } } // namespace diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index ed3725bc0..f61bd2374 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1302,7 +1302,7 @@ std::unique_ptr HistoryInner::prepareDrag() { const auto pressedHandler = ClickHandler::getPressed(); if (dynamic_cast(pressedHandler.get()) - || !_peer->allowsForwarding()) { + || hasCopyRestriction()) { return nullptr; } @@ -1546,7 +1546,7 @@ void HistoryInner::mouseActionFinish( if (QGuiApplication::clipboard()->supportsSelection() && !_selected.empty() && _selected.cbegin()->second != FullSelection - && _peer->allowsForwarding()) { + && !hasCopyRestriction()) { const auto [item, selection] = *_selected.cbegin(); if (const auto view = item->mainView()) { TextUtilities::SetClipboardText( @@ -1725,7 +1725,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }; const auto addPhotoActions = [&](not_null photo) { const auto media = photo->activeMediaView(); - if (!photo->isNull() && media && media->loaded() && _peer->allowsForwarding()) { + if (!photo->isNull() && media && media->loaded() && !hasCopyRestriction()) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { savePhotoToFile(photo); })); @@ -1767,16 +1767,18 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { openContextGif(itemId); }); } - _menu->addAction(tr::lng_context_save_gif(tr::now), [=] { - saveContextGif(itemId); - }); + if (!hasCopyRestriction()) { + _menu->addAction(tr::lng_context_save_gif(tr::now), [=] { + saveContextGif(itemId); + }); + } } if (!document->filepath(true).isEmpty()) { _menu->addAction(Platform::IsMac() ? tr::lng_context_show_in_finder(tr::now) : tr::lng_context_show_in_folder(tr::now), [=] { showContextInFolder(document); }); } - if (_peer->allowsForwarding()) { + if (!hasCopyRestriction()) { _menu->addAction(lnkIsVideo ? tr::lng_context_save_video(tr::now) : (lnkIsVoice ? tr::lng_context_save_audio(tr::now) : (lnkIsAudio ? tr::lng_context_save_audio_file(tr::now) : tr::lng_context_save_file(tr::now))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { saveDocumentToFile(itemId, document); })); @@ -1830,7 +1832,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (lnkPhoto || lnkDocument) { const auto item = _dragStateItem; const auto itemId = item ? item->fullId() : FullMsgId(); - if (isUponSelected > 0 && _peer->allowsForwarding()) { + if (isUponSelected > 0 && !hasCopyRestriction()) { _menu->addAction( (isUponSelected > 1 ? tr::lng_context_copy_selected_items(tr::now) @@ -1911,7 +1913,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto view = item ? item->mainView() : nullptr; if (isUponSelected > 0) { - if (_peer->allowsForwarding()) { + if (!hasCopyRestriction()) { _menu->addAction( ((isUponSelected > 1) ? tr::lng_context_copy_selected_items(tr::now) @@ -1934,7 +1936,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { Api::ToggleFavedSticker(document, itemId); }); } - if (_peer->allowsForwarding()) { + if (!hasCopyRestriction()) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { saveDocumentToFile(itemId, document); })); @@ -1963,7 +1965,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (!item->isService() && view && !link - && _peer->allowsForwarding() + && !hasCopyRestriction() && (view->hasVisibleText() || mediaHasTextForCopy)) { _menu->addAction(tr::lng_context_copy_text(tr::now), [=] { copyContextText(itemId); @@ -2135,7 +2137,9 @@ void HistoryInner::openContextGif(FullMsgId itemId) { } void HistoryInner::saveContextGif(FullMsgId itemId) { - if (const auto item = session().data().message(itemId)) { + if (hasCopyRestriction()) { + return; + } else if (const auto item = session().data().message(itemId)) { if (const auto media = item->media()) { if (const auto document = media->document()) { Api::ToggleSavedGif(document, item->fullId(), true); diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 9bc30dd53..e39bfa6d3 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -241,7 +241,7 @@ void AddDocumentActions( OpenGif(list->controller(), contextId); }); } - if (document->isGifv()) { + if (document->isGifv() && !list->hasCopyRestriction()) { menu->addAction(tr::lng_context_save_gif(tr::now), [=] { SaveGif(list->controller(), contextId); }); diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 502e7b487..5ef488dbb 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -329,7 +329,7 @@ void Document::draw( const auto cornerDownload = downloadInCorner(); - if (!_dataMedia->canBePlayed()) { + if (!_dataMedia->canBePlayed(_realParent)) { _dataMedia->automaticLoad(_realParent->fullId(), _realParent); } bool loaded = dataLoaded(), displayLoading = _data->displayLoading(); @@ -452,8 +452,8 @@ void Document::draw( return _data->isSongWithCover() ? sti->historyFileThumbPause : stm->historyFilePause; - } else if (loaded || _dataMedia->canBePlayed()) { - return _dataMedia->canBePlayed() + } else if (loaded || _dataMedia->canBePlayed(_realParent)) { + return _dataMedia->canBePlayed(_realParent) ? (_data->isSongWithCover() ? sti->historyFileThumbPlay : stm->historyFilePlay) @@ -593,7 +593,8 @@ void Document::ensureDataMediaCreated() const { bool Document::downloadInCorner() const { return _data->isAudioFile() - && _data->canBeStreamed() + && _realParent->history()->peer->allowsForwarding() + && _data->canBeStreamed(_realParent) && !_data->inappPlaybackFailed() && _realParent->isRegular(); } @@ -782,7 +783,7 @@ TextState Document::textState( && (!_data->loading() || downloadInCorner()) && !_data->uploading() && !_data->isNull()) { - if (loaded || _dataMedia->canBePlayed()) { + if (loaded || _dataMedia->canBePlayed(_realParent)) { result.link = _openl; } else { result.link = _savel; diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 75772a124..6b5d07b9a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -264,7 +264,8 @@ QSize Gif::videoSize() const { bool Gif::downloadInCorner() const { return _data->isVideoFile() && (_data->loading() || !autoplayEnabled()) - && _data->canBeStreamed() + && _realParent->history()->peer->allowsForwarding() + && _data->canBeStreamed(_realParent) && !_data->inappPlaybackFailed() && !_parent->data()->isSending(); } @@ -288,7 +289,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const { const auto stm = context.messageStyle(); const auto autoPaused = _parent->delegate()->elementIsGifPaused(); const auto cornerDownload = downloadInCorner(); - const auto canBePlayed = _dataMedia->canBePlayed(); + const auto canBePlayed = _dataMedia->canBePlayed(_realParent); const auto autoplay = autoplayEnabled() && canBePlayed && CanPlayInline(_data); @@ -836,7 +837,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const { ? _cancell : _realParent->isSending() ? nullptr - : (dataLoaded() || _dataMedia->canBePlayed()) + : (dataLoaded() || _dataMedia->canBePlayed(_realParent)) ? _openl : _data->loading() ? _cancell @@ -917,7 +918,7 @@ void Gif::drawGrouped( const auto autoPaused = _parent->delegate()->elementIsGifPaused(); const auto fullFeatured = fullFeaturedGrouped(sides); const auto cornerDownload = fullFeatured && downloadInCorner(); - const auto canBePlayed = _dataMedia->canBePlayed(); + const auto canBePlayed = _dataMedia->canBePlayed(_realParent); const auto autoplay = fullFeatured && autoplayEnabled() && canBePlayed @@ -1109,7 +1110,7 @@ TextState Gif::getStateGrouped( ? _cancell : _realParent->isSending() ? nullptr - : (dataLoaded() || _dataMedia->canBePlayed()) + : (dataLoaded() || _dataMedia->canBePlayed(_realParent)) ? _openl : _data->loading() ? _cancell @@ -1258,7 +1259,7 @@ void Gif::updateStatusText() const { statusSize = _data->uploadingData->offset; } else if (!downloadInCorner() && _data->loading()) { statusSize = _data->loadOffset(); - } else if (dataLoaded() || _dataMedia->canBePlayed()) { + } else if (dataLoaded() || _dataMedia->canBePlayed(_realParent)) { statusSize = Ui::FileStatusSizeLoaded; } else { statusSize = Ui::FileStatusSizeReady; @@ -1386,7 +1387,7 @@ void Gif::playAnimation(bool autoplay) { } if (_streamed) { stopAnimation(); - } else if (_dataMedia->canBePlayed()) { + } else if (_dataMedia->canBePlayed(_realParent)) { if (!autoplayEnabled()) { history()->owner().checkPlayingAnimations(); } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index 811d93d39..4b764bc25 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -190,7 +190,7 @@ ClickHandlerPtr ItemBase::getResultPreviewHandler() const { _result->_content_url, false); } else if (const auto document = _result->_document - ; document && document->createMediaView()->canBePlayed()) { + ; document && document->createMediaView()->canBePlayed(nullptr)) { return std::make_shared(); } else if (_result->_photo) { return std::make_shared(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 6d4e8df8f..b6e36b268 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1212,7 +1212,7 @@ bool OverlayWidget::radialAnimationCallback(crl::time now) { update(radialRect()); } const auto ready = _document && _documentMedia->loaded(); - const auto streamVideo = ready && _documentMedia->canBePlayed(); + const auto streamVideo = ready && _documentMedia->canBePlayed(_message); const auto tryOpenImage = ready && (_document->size < Images::kReadBytesLimit); if (ready && ((tryOpenImage && !_radial.animating()) || streamVideo)) { @@ -1693,7 +1693,7 @@ void OverlayWidget::downloadMedia() { void OverlayWidget::saveCancel() { if (_document && _document->loading()) { _document->cancel(); - if (_documentMedia->canBePlayed()) { + if (_documentMedia->canBePlayed(_message)) { redisplayContent(); } } @@ -2429,7 +2429,7 @@ void OverlayWidget::displayDocument( ).toImage()); } } else { - if (_documentMedia->canBePlayed() + if (_documentMedia->canBePlayed(_message) && initStreaming(continueStreaming)) { } else if (_document->isVideoFile()) { _documentMedia->automaticLoad(fileOrigin(), _message); @@ -2577,7 +2577,7 @@ void OverlayWidget::displayFinished() { } bool OverlayWidget::canInitStreaming() const { - return (_document && _documentMedia->canBePlayed()) + return (_document && _documentMedia->canBePlayed(_message)) || (_photo && _photo->videoCanBePlayed()); } @@ -4106,7 +4106,7 @@ void OverlayWidget::preloadData(int delta) { const auto [i, ok] = documents.emplace( (*document)->createMediaView()); (*i)->thumbnailWanted(fileOrigin(entity)); - if (!(*i)->canBePlayed()) { + if (!(*i)->canBePlayed(entity.item)) { (*i)->automaticLoad(fileOrigin(entity), entity.item); } } diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 06e1ac499..e20eff936 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -484,7 +484,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const if (!selected && !context->selecting && radialOpacity < 1.) { if (clip.intersects(QRect(0, _height - st::normalFont->height, _width, st::normalFont->height))) { - const auto download = !loaded && !_dataMedia->canBePlayed(); + const auto download = !loaded && !_dataMedia->canBePlayed(parent()); const auto &icon = download ? (selected ? st::overviewVideoDownloadSelected : st::overviewVideoDownload) : (selected ? st::overviewVideoPlaySelected : st::overviewVideoPlay); @@ -510,7 +510,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const if (selected) { p.setBrush(st::msgDateImgBgSelected); } else { - auto over = ClickHandler::showAsActive((_data->loading() || _data->uploading()) ? _cancell : (loaded || _dataMedia->canBePlayed()) ? _openl : _savel); + auto over = ClickHandler::showAsActive((_data->loading() || _data->uploading()) ? _cancell : (loaded || _dataMedia->canBePlayed(parent())) ? _openl : _savel); p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, _a_iconOver.value(over ? 1. : 0.))); } @@ -576,7 +576,7 @@ TextState Video::getState( ensureDataMediaCreated(); const auto link = (_data->loading() || _data->uploading()) ? _cancell - : (dataLoaded() || _dataMedia->canBePlayed()) + : (dataLoaded() || _dataMedia->canBePlayed(parent())) ? _openl : _savel; return { parent(), link }; @@ -704,7 +704,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const } const auto &checkLink = (_data->loading() || _data->uploading()) ? _cancell - : (_dataMedia->canBePlayed() || loaded) + : (_dataMedia->canBePlayed(parent()) || loaded) ? _openl : _savel; if (selected) { @@ -732,7 +732,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const return &(selected ? _st.voiceCancelSelected : _st.voiceCancel); } else if (showPause) { return &(selected ? _st.voicePauseSelected : _st.voicePause); - } else if (_dataMedia->canBePlayed()) { + } else if (_dataMedia->canBePlayed(parent())) { return &(selected ? _st.voicePlaySelected : _st.voicePlay); } return &(selected @@ -802,7 +802,7 @@ TextState Voice::getState( if (inner.contains(point)) { const auto link = (_data->loading() || _data->uploading()) ? _cancell - : (_dataMedia->canBePlayed() || loaded) + : (_dataMedia->canBePlayed(parent()) || loaded) ? _openl : _savel; return { parent(), link }; @@ -969,7 +969,8 @@ Document::Document( bool Document::downloadInCorner() const { return _data->isAudioFile() - && _data->canBeStreamed() + && parent()->history()->peer->allowsForwarding() + && _data->canBeStreamed(parent()) && !_data->inappPlaybackFailed() && parent()->isRegular(); } @@ -1033,7 +1034,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con } else { const auto over = ClickHandler::showAsActive(isLoading ? _cancell - : (loaded || _dataMedia->canBePlayed()) + : (loaded || _dataMedia->canBePlayed(parent())) ? _openl : _savel); p.setBrush(anim::brush( @@ -1055,7 +1056,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con return &(selected ? _st.voicePauseSelected : _st.voicePause); - } else if (loaded || _dataMedia->canBePlayed()) { + } else if (loaded || _dataMedia->canBePlayed(parent())) { return &(selected ? _st.voicePlaySelected : _st.voicePlay); @@ -1068,7 +1069,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con return &(selected ? _st.songCancelSelected : _st.songCancel); } else if (showPause) { return &(selected ? _st.songPauseSelected : _st.songPause); - } else if (loaded || _dataMedia->canBePlayed()) { + } else if (loaded || _dataMedia->canBePlayed(parent())) { return &(selected ? _st.songPlaySelected : _st.songPlay); } return &(selected ? _st.songDownloadSelected : _st.songDownload); @@ -1280,7 +1281,7 @@ TextState Document::getState( const auto link = (!downloadInCorner() && (_data->loading() || _data->uploading())) ? _cancell - : (loaded || _dataMedia->canBePlayed()) + : (loaded || _dataMedia->canBePlayed(parent())) ? _openl : _savel; return { parent(), link }; From 4691cff3f604a4a99ba97a26d56f7649c38b36f7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 16:10:51 +0400 Subject: [PATCH 034/180] Start SendAsButton in HistoryWidget. --- Telegram/CMakeLists.txt | 2 + .../SourceFiles/data/data_peer_values.cpp | 50 ++++++++++++ Telegram/SourceFiles/data/data_peer_values.h | 10 +++ .../SourceFiles/history/history_widget.cpp | 63 ++++++++++++++- Telegram/SourceFiles/history/history_widget.h | 4 + Telegram/SourceFiles/main/main_session.cpp | 2 + Telegram/SourceFiles/main/main_session.h | 5 ++ .../main/session/send_as_peers.cpp | 78 +++++++++++++++++++ .../SourceFiles/main/session/send_as_peers.h | 40 ++++++++++ Telegram/SourceFiles/ui/chat/chat.style | 25 ++++++ .../ui/controls/send_as_button.cpp | 70 +++++++++++++++++ .../SourceFiles/ui/controls/send_as_button.h | 39 ++++++++++ .../SourceFiles/ui/controls/send_button.h | 2 +- Telegram/cmake/td_ui.cmake | 2 + Telegram/lib_rpl | 2 +- 15 files changed, 390 insertions(+), 4 deletions(-) create mode 100644 Telegram/SourceFiles/main/session/send_as_peers.cpp create mode 100644 Telegram/SourceFiles/main/session/send_as_peers.h create mode 100644 Telegram/SourceFiles/ui/controls/send_as_button.cpp create mode 100644 Telegram/SourceFiles/ui/controls/send_as_button.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index dc71724c2..9c67ebcfb 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -785,6 +785,8 @@ PRIVATE main/main_session.h main/main_session_settings.cpp main/main_session_settings.h + main/session/send_as_peers.cpp + main/session/send_as_peers.h media/system_media_controls_manager.h media/system_media_controls_manager.cpp media/audio/media_audio.cpp diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 110afa755..668cad623 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -11,6 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_user.h" +#include "data/data_changes.h" +#include "main/main_session.h" +#include "ui/image/image_prepare.h" #include "base/unixtime.h" namespace Data { @@ -445,4 +448,51 @@ bool ChannelHasActiveCall(not_null channel) { return (channel->flags() & ChannelDataFlag::CallNotEmpty); } +rpl::producer PeerUserpicImageValue( + not_null peer, + int size) { + return PeerUserpicImageValue(peer, size, ImageRoundRadius::Ellipse); +} + +rpl::producer PeerUserpicImageValue( + not_null peer, + int size, + ImageRoundRadius radius) { + return [=](auto consumer) { + auto result = rpl::lifetime(); + struct State { + std::shared_ptr view; + rpl::lifetime waiting; + InMemoryKey key = {}; + bool empty = true; + Fn push; + }; + const auto state = result.make_state(); + state->push = [=] { + const auto key = peer->userpicUniqueKey(state->view); + const auto loading = !state->view || state->view->image(); + + if (loading && !state->waiting) { + peer->session().downloaderTaskFinished( + ) | rpl::start_with_next(state->push, state->waiting); + } else if (!loading && state->waiting) { + state->waiting.destroy(); + } + + if (!state->empty && (loading || key == state->key)) { + return; + } + state->key = key; + state->empty = false; + consumer.put_next( + peer->generateUserpicImage(state->view, size, radius)); + }; + peer->session().changes().peerFlagsValue( + peer, + PeerUpdate::Flag::Photo + ) | rpl::start_with_next(state->push, result); + return result; + }; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_peer_values.h b/Telegram/SourceFiles/data/data_peer_values.h index b371d242c..31cbcb6b3 100644 --- a/Telegram/SourceFiles/data/data_peer_values.h +++ b/Telegram/SourceFiles/data/data_peer_values.h @@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include "data/data_peer.h" +enum class ImageRoundRadius; + namespace Data { template @@ -110,4 +112,12 @@ inline auto PeerFullFlagValue( [[nodiscard]] bool IsUserOnline(not_null user); [[nodiscard]] bool ChannelHasActiveCall(not_null channel); +[[nodiscard]] rpl::producer PeerUserpicImageValue( + not_null peer, + int size); +[[nodiscard]] rpl::producer PeerUserpicImageValue( + not_null peer, + int size, + ImageRoundRadius radius); + } // namespace Data diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 292804630..ba56c4573 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/special_buttons.h" #include "ui/controls/emoji_button.h" #include "ui/controls/send_button.h" +#include "ui/controls/send_as_button.h" #include "inline_bots/inline_bot_result.h" #include "base/event_filter.h" #include "base/qt_signal_producer.h" @@ -52,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_web_page.h" #include "data/data_document.h" #include "data/data_photo.h" +#include "data/data_peer_values.h" #include "data/data_media_types.h" #include "data/data_channel.h" #include "data/data_chat.h" @@ -120,6 +122,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/unread_badge.h" #include "main/main_session.h" #include "main/main_session_settings.h" +#include "main/session/send_as_peers.h" #include "window/notifications_manager.h" #include "window/window_adaptive.h" #include "window/window_controller.h" @@ -791,6 +794,7 @@ HistoryWidget::HistoryWidget( }, lifetime()); setupScheduledToggle(); + setupSendAsToggle(); orderWidgets(); setupShortcuts(); } @@ -2111,6 +2115,7 @@ void HistoryWidget::showHistory( updateNotifyControls(); } refreshScheduledToggle(); + refreshSendAsToggle(); if (_showAtMsgId == ShowAtUnreadMsgId) { if (_history->scrollTopItem) { @@ -2368,6 +2373,49 @@ void HistoryWidget::refreshScheduledToggle() { } } +void HistoryWidget::setupSendAsToggle() { + session().sendAsPeers().updated( + ) | rpl::filter([=](not_null peer) { + return (peer == _peer); + }) | rpl::start_with_next([=] { + refreshSendAsToggle(); + updateControlsVisibility(); + updateControlsGeometry(); + }, lifetime()); +} + +void HistoryWidget::refreshSendAsToggle() { + Expects(_peer != nullptr); + + session().sendAsPeers().refresh(_peer); + const auto &list = session().sendAsPeers().list(_peer); + const auto has = _peer->canWrite() && (list.size() > 1); + if (!has) { + _sendAs.destroy(); + return; + } else if (_sendAs) { + return; + } + _sendAs.create(this, st::sendAsButton); + + using namespace rpl::mappers; + controller()->activeChatValue( + ) | rpl::map([=](const Dialogs::Key &key) -> PeerData* { + if (const auto history = key.history()) { + return history->peer; + } + return nullptr; + }) | rpl::filter_nullptr( + ) | rpl::map([=](not_null peer) { + return Data::PeerUserpicImageValue( + peer, + st::sendAsButton.size * style::DevicePixelRatio()); + }) | rpl::flatten_latest( + ) | rpl::start_with_next([=](QImage &&userpic) { + _sendAs->setUserpic(std::move(userpic)); + }, _sendAs->lifetime()); +} + bool HistoryWidget::contentOverlapped(const QRect &globalRect) { return (_attachDragAreas.document->overlaps(globalRect) || _attachDragAreas.photo->overlaps(globalRect) @@ -2469,6 +2517,9 @@ void HistoryWidget::updateControlsVisibility() { if (_ttlInfo) { _ttlInfo->hide(); } + if (_sendAs) { + _sendAs->hide(); + } _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); @@ -2535,6 +2586,9 @@ void HistoryWidget::updateControlsVisibility() { if (_ttlInfo) { _ttlInfo->show(); } + if (_sendAs) { + _sendAs->show(); + } updateFieldPlaceholder(); if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { @@ -2567,9 +2621,11 @@ void HistoryWidget::updateControlsVisibility() { if (_ttlInfo) { _ttlInfo->hide(); } + if (_sendAs) { + _sendAs->hide(); + } _kbScroll->hide(); _fieldBarCancel->hide(); - _attachToggle->hide(); _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -4345,13 +4401,16 @@ void HistoryWidget::moveFieldControls() { _kbScroll->setGeometryToLeft(0, bottom, width(), keyboardHeight); } -// _attachToggle --------- _inlineResults -------------------------------------- _tabbedPanel --------- _fieldBarCancel +// _attachToggle (_sendAs) ------- _inlineResults ---------------------------------- _tabbedPanel -------- _fieldBarCancel // (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send // (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages) auto buttonsBottom = bottom - _attachToggle->height(); auto left = st::historySendRight; _attachToggle->moveToLeft(left, buttonsBottom); left += _attachToggle->width(); + if (_sendAs) { + _sendAs->moveToLeft(left, buttonsBottom); left += _sendAs->width(); + } _field->moveToLeft(left, bottom - _field->height() - st::historySendPadding); auto right = st::historySendRight; _send->moveToRight(right, buttonsBottom); right += _send->width(); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 74a8a05f5..3406eda25 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -76,6 +76,7 @@ class GroupCallBar; class RequestsBar; struct PreparedList; class SendFilesWay; +class SendAsButton; enum class ReportReason; namespace Toast { class Instance; @@ -609,6 +610,8 @@ private: void setupScheduledToggle(); void refreshScheduledToggle(); + void setupSendAsToggle(); + void refreshSendAsToggle(); bool kbWasHidden() const; @@ -718,6 +721,7 @@ private: object_ptr _muteUnmute; object_ptr _reportMessages; object_ptr _attachToggle; + object_ptr _sendAs = { nullptr }; object_ptr _tabbedSelectorToggle; object_ptr _botKeyboardShow; object_ptr _botKeyboardHide; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index bdd5b40bb..4507d75d1 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_account.h" #include "main/main_domain.h" #include "main/main_session_settings.h" +#include "main/session/send_as_peers.h" #include "mtproto/mtproto_config.h" #include "chat_helpers/stickers_emoji_pack.h" #include "chat_helpers/stickers_dice_pack.h" @@ -80,6 +81,7 @@ Session::Session( , _user(_data->processUser(user)) , _emojiStickersPack(std::make_unique(this)) , _diceStickersPacks(std::make_unique(this)) +, _sendAsPeers(std::make_unique(this)) , _supportHelper(Support::Helper::Create(this)) , _saveSettingsTimer([=] { saveSettings(); }) { Expects(_settings != nullptr); diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 01142396a..44693c498 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -58,6 +58,7 @@ namespace Main { class Account; class Domain; class SessionSettings; +class SendAsPeers; class Session final : public base::has_weak_ptr { public: @@ -113,6 +114,9 @@ public: [[nodiscard]] SessionSettings &settings() const { return *_settings; } + [[nodiscard]] SendAsPeers &sendAsPeers() const { + return *_sendAsPeers; + } void saveSettings(); void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay); @@ -180,6 +184,7 @@ private: // _emojiStickersPack depends on _data. const std::unique_ptr _emojiStickersPack; const std::unique_ptr _diceStickersPacks; + const std::unique_ptr _sendAsPeers; const std::unique_ptr _supportHelper; diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp new file mode 100644 index 000000000..a381f3cc1 --- /dev/null +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -0,0 +1,78 @@ +/* +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 "main/session/send_as_peers.h" + +#include "data/data_user.h" +#include "data/data_session.h" +#include "main/main_session.h" +#include "apiwrap.h" + +namespace Main { +namespace { + +constexpr auto kRequestEach = 30 * crl::time(1000); + +} // namespace + +SendAsPeers::SendAsPeers(not_null session) +: _session(session) +, _onlyMe({ session->user() }) { +} + +void SendAsPeers::refresh(not_null peer) { + if (!peer->isMegagroup()) { + return; + } + const auto now = crl::now(); + const auto i = _lastRequestTime.find(peer); + const auto when = (i == end(_lastRequestTime)) ? -1 : i->second; + if (when >= 0 && now < when + kRequestEach) { + return; + } + _lastRequestTime[peer] = now; + request(peer); +} + +const std::vector> &SendAsPeers::list(not_null peer) { + const auto i = _lists.find(peer); + return (i != end(_lists)) ? i->second : _onlyMe; +} + +rpl::producer> SendAsPeers::updated() const { + return _updates.events(); +} + +void SendAsPeers::request(not_null peer) { + _session->api().request(MTPchannels_GetSendAs( + peer->input + )).done([=](const MTPchannels_SendAsPeers &result) { + auto list = std::vector>(); + auto &owner = _session->data(); + result.match([&](const MTPDchannels_sendAsPeers &data) { + owner.processUsers(data.vusers()); + owner.processChats(data.vchats()); + for (const auto &id : data.vpeers().v) { + if (const auto peer = owner.peerLoaded(peerFromMTP(id))) { + list.push_back(peer); + } + } + }); + if (list.size() > 1) { + auto &now = _lists[peer]; + if (now != list) { + now = std::move(list); + _updates.fire_copy(peer); + } + } else if (const auto i = _lists.find(peer); i != end(_lists)) { + _lists.erase(i); + _updates.fire_copy(peer); + } + }).send(); +} + +} // namespace Main diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h new file mode 100644 index 000000000..951038a72 --- /dev/null +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -0,0 +1,40 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class PeerData; + +namespace Main { + +class Session; + +class SendAsPeers final { +public: + explicit SendAsPeers(not_null session); + + void refresh(not_null peer); + [[nodiscard]] const std::vector> &list( + not_null peer); + [[nodiscard]] rpl::producer> updated() const; + +private: + void request(not_null peer); + + const not_null _session; + const std::vector> _onlyMe; + + base::flat_map< + not_null, + std::vector>> _lists; + base::flat_map, crl::time> _lastRequestTime; + + rpl::event_stream> _updates; + +}; + +} // namespace Main diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index e856d8158..81b45668b 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -917,3 +917,28 @@ historyRequestsUserpics: GroupCallUserpics { align: align(left); } historyRequestsHeight: 33px; + +SendAsButton { + width: pixels; + height: pixels; + size: pixels; + activeBg: color; + activeFg: color; + cross: CrossAnimation; + duration: int; +} + +sendAsButton: SendAsButton { + width: 44px; + height: 46px; + size: 32px; + activeBg: activeButtonBg; + activeFg: activeButtonFg; + cross: CrossAnimation { + size: 32px; + skip: 10px; + stroke: 2px; + minScale: 0.3; + } + duration: 150; +} diff --git a/Telegram/SourceFiles/ui/controls/send_as_button.cpp b/Telegram/SourceFiles/ui/controls/send_as_button.cpp new file mode 100644 index 000000000..b9a124176 --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/send_as_button.cpp @@ -0,0 +1,70 @@ +/* +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 "ui/controls/send_as_button.h" + +#include "ui/effects/cross_animation.h" +#include "styles/style_chat.h" + +namespace Ui { + +SendAsButton::SendAsButton(QWidget *parent, const style::SendAsButton &st) +: AbstractButton(parent) +, _st(st) { + resize(_st.width, _st.height); +} + + +void SendAsButton::setUserpic(QImage userpic) { + _userpic = std::move(userpic); + update(); +} + +void SendAsButton::setActive(bool active) { + if (_active == active) { + return; + } + _active = active; + _activeAnimation.start( + [=] { update(); }, + _active ? 0. : 1., + _active ? 1. : 0., + _st.duration); +} + +void SendAsButton::paintEvent(QPaintEvent *e) { + auto p = Painter(this); + + const auto left = (width() - _st.size) / 2; + const auto top = (height() - _st.size) / 2; + + const auto active = _activeAnimation.value(_active ? 1. : 0.); + if (active < 1. && !_userpic.isNull()) { + p.drawImage(left, top, _userpic); + } + if (active > 0.) { + p.setOpacity(active); + + p.setPen(Qt::NoPen); + p.setBrush(_st.activeBg); + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(left, top, _st.size, _st.size); + } + + CrossAnimation::paint( + p, + _st.cross, + _st.activeFg, + left, + top, + width(), + active); + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/send_as_button.h b/Telegram/SourceFiles/ui/controls/send_as_button.h new file mode 100644 index 000000000..d5eb47c3a --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/send_as_button.h @@ -0,0 +1,39 @@ +/* +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 "ui/widgets/buttons.h" +#include "ui/effects/animations.h" + +namespace style { +struct SendAsButton; +} // namespace style + +namespace Ui { + +class SendAsButton final : public AbstractButton { +public: + SendAsButton(QWidget *parent, const style::SendAsButton &st); + + void setUserpic(QImage userpic); + + void setActive(bool active); + +private: + void paintEvent(QPaintEvent *e) override; + + const style::SendAsButton &_st; + + Animations::Simple _activeAnimation; + bool _active = false; + + QImage _userpic; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/send_button.h b/Telegram/SourceFiles/ui/controls/send_button.h index da67bcbf5..0c174c8b3 100644 --- a/Telegram/SourceFiles/ui/controls/send_button.h +++ b/Telegram/SourceFiles/ui/controls/send_button.h @@ -13,7 +13,7 @@ namespace Ui { class SendButton final : public RippleButton { public: - SendButton(QWidget *parent); + explicit SendButton(QWidget *parent); static constexpr auto kSlowmodeDelayLimit = 100 * 60; diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index a087769b5..20d23d0d7 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -179,6 +179,8 @@ PRIVATE ui/controls/invite_link_buttons.h ui/controls/invite_link_label.cpp ui/controls/invite_link_label.h + ui/controls/send_as_button.cpp + ui/controls/send_as_button.h ui/controls/send_button.cpp ui/controls/send_button.h ui/controls/who_read_context_action.cpp diff --git a/Telegram/lib_rpl b/Telegram/lib_rpl index df721be3f..94a42b775 160000 --- a/Telegram/lib_rpl +++ b/Telegram/lib_rpl @@ -1 +1 @@ -Subproject commit df721be3fa14a27dfc230d2e3c42bb1a7c9d0617 +Subproject commit 94a42b775ab4e46e5edeb88d8ed6c06f9e869c61 From 1bd74fe47891b2c5c99bd64f8607211d3e2f7114 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 17:50:33 +0400 Subject: [PATCH 035/180] Choose a channel to send messages as. --- Telegram/SourceFiles/data/data_channel.cpp | 8 + .../SourceFiles/data/data_peer_values.cpp | 2 +- .../SourceFiles/history/history_widget.cpp | 20 +- .../main/session/send_as_peers.cpp | 55 ++++- .../SourceFiles/main/session/send_as_peers.h | 14 +- .../SourceFiles/ui/chat/choose_send_as.cpp | 210 ++++++++++++++++++ Telegram/SourceFiles/ui/chat/choose_send_as.h | 33 +++ 7 files changed, 319 insertions(+), 23 deletions(-) create mode 100644 Telegram/SourceFiles/ui/chat/choose_send_as.cpp create mode 100644 Telegram/SourceFiles/ui/chat/choose_send_as.h diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 532918314..a6b6a95ca 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_location.h" #include "data/data_histories.h" #include "data/data_group_call.h" +#include "main/main_session.h" +#include "main/session/send_as_peers.h" #include "base/unixtime.h" #include "history/history.h" #include "main/main_session.h" @@ -915,6 +917,12 @@ void ApplyChannelUpdate( MTP_inputNotifyPeer(channel->input), update.vnotify_settings()); + if (const auto sendAs = update.vdefault_send_as()) { + session->sendAsPeers().setChosen(channel, peerFromMTP(*sendAs)); + } else { + session->sendAsPeers().setChosen(channel, PeerId()); + } + // For clearUpTill() call. channel->owner().sendHistoryChangeNotifications(); } diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 668cad623..a82493ac2 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -470,7 +470,7 @@ rpl::producer PeerUserpicImageValue( const auto state = result.make_state(); state->push = [=] { const auto key = peer->userpicUniqueKey(state->view); - const auto loading = !state->view || state->view->image(); + const auto loading = state->view && !state->view->image(); if (loading && !state->waiting) { peer->session().downloaderTaskFinished( diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index ba56c4573..9bcaa4d54 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/forward_options_box.h" #include "ui/chat/message_bar.h" #include "ui/chat/attach/attach_send_files_way.h" +#include "ui/chat/choose_send_as.h" #include "ui/image/image.h" #include "ui/special_buttons.h" #include "ui/controls/emoji_button.h" @@ -53,7 +54,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_web_page.h" #include "data/data_document.h" #include "data/data_photo.h" -#include "data/data_peer_values.h" #include "data/data_media_types.h" #include "data/data_channel.h" #include "data/data_chat.h" @@ -2397,23 +2397,7 @@ void HistoryWidget::refreshSendAsToggle() { return; } _sendAs.create(this, st::sendAsButton); - - using namespace rpl::mappers; - controller()->activeChatValue( - ) | rpl::map([=](const Dialogs::Key &key) -> PeerData* { - if (const auto history = key.history()) { - return history->peer; - } - return nullptr; - }) | rpl::filter_nullptr( - ) | rpl::map([=](not_null peer) { - return Data::PeerUserpicImageValue( - peer, - st::sendAsButton.size * style::DevicePixelRatio()); - }) | rpl::flatten_latest( - ) | rpl::start_with_next([=](QImage &&userpic) { - _sendAs->setUserpic(std::move(userpic)); - }, _sendAs->lifetime()); + Ui::SetupSendAsButton(_sendAs.data(), controller()); } bool HistoryWidget::contentOverlapped(const QRect &globalRect) { diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index a381f3cc1..41ebdd2f3 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -38,7 +38,8 @@ void SendAsPeers::refresh(not_null peer) { request(peer); } -const std::vector> &SendAsPeers::list(not_null peer) { +const std::vector> &SendAsPeers::list( + not_null peer) const { const auto i = _lists.find(peer); return (i != end(_lists)) ? i->second : _onlyMe; } @@ -47,12 +48,60 @@ rpl::producer> SendAsPeers::updated() const { return _updates.events(); } +void SendAsPeers::saveChosen( + not_null peer, + not_null chosen) { + peer->session().api().request(MTPmessages_SaveDefaultSendAs( + peer->input, + chosen->input + )).send(); + + setChosen(peer, chosen->id); +} + +void SendAsPeers::setChosen(not_null peer, PeerId chosenId) { + if (chosen(peer) == chosenId) { + return; + } + const auto fallback = peer->amAnonymous() + ? peer + : peer->session().user(); + if (fallback->id == chosenId) { + _chosen.remove(peer); + } else { + _chosen[peer] = chosenId; + } + _updates.fire_copy(peer); +} + +PeerId SendAsPeers::chosen(not_null peer) const { + const auto i = _chosen.find(peer); + return (i != end(_chosen)) ? i->second : PeerId(); +} + +not_null SendAsPeers::resolveChosen( + not_null peer) const { + return ResolveChosen(peer, list(peer), chosen(peer)); +} + +not_null SendAsPeers::ResolveChosen( + not_null peer, + const std::vector> &list, + PeerId chosen) { + const auto i = ranges::find(list, chosen, &PeerData::id); + return (i != end(list)) + ? (*i) + : (peer->isMegagroup() && peer->amAnonymous()) + ? peer + : peer->session().user(); +} + void SendAsPeers::request(not_null peer) { - _session->api().request(MTPchannels_GetSendAs( + peer->session().api().request(MTPchannels_GetSendAs( peer->input )).done([=](const MTPchannels_SendAsPeers &result) { auto list = std::vector>(); - auto &owner = _session->data(); + auto &owner = peer->owner(); result.match([&](const MTPDchannels_sendAsPeers &data) { owner.processUsers(data.vusers()); owner.processChats(data.vchats()); diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h index 951038a72..46d2ad764 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.h +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -19,9 +19,20 @@ public: void refresh(not_null peer); [[nodiscard]] const std::vector> &list( - not_null peer); + not_null peer) const; [[nodiscard]] rpl::producer> updated() const; + void saveChosen(not_null peer, not_null chosen); + void setChosen(not_null peer, PeerId chosenId); + [[nodiscard]] PeerId chosen(not_null peer) const; + [[nodiscard]] not_null resolveChosen( + not_null peer) const; + + [[nodiscard]] static not_null ResolveChosen( + not_null peer, + const std::vector> &list, + PeerId chosen); + private: void request(not_null peer); @@ -32,6 +43,7 @@ private: not_null, std::vector>> _lists; base::flat_map, crl::time> _lastRequestTime; + base::flat_map, PeerId> _chosen; rpl::event_stream> _updates; diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp new file mode 100644 index 000000000..1a0b9681d --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -0,0 +1,210 @@ +/* +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 "ui/chat/choose_send_as.h" + +#include "boxes/peer_list_box.h" +#include "data/data_peer.h" +#include "data/data_channel.h" +#include "data/data_peer_values.h" +#include "history/history.h" +#include "ui/controls/send_as_button.h" +#include "window/window_session_controller.h" +#include "main/main_session.h" +#include "main/session/send_as_peers.h" +#include "lang/lang_keys.h" +#include "styles/style_calls.h" +#include "styles/style_boxes.h" +#include "styles/style_chat.h" + +namespace Ui { +namespace { + +class ListController final : public PeerListController { +public: + ListController( + std::vector> list, + not_null selected); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + + [[nodiscard]] not_null selected() const; + +private: + std::unique_ptr createRow(not_null peer); + + std::vector> _list; + not_null _selected; + +}; + +ListController::ListController( + std::vector> list, + not_null selected) +: PeerListController() +, _list(std::move(list)) +, _selected(selected) { +} + +Main::Session &ListController::session() const { + return _selected->session(); +} + +std::unique_ptr ListController::createRow( + not_null peer) { + auto result = std::make_unique(peer); + if (peer->isSelf()) { + result->setCustomStatus( + tr::lng_group_call_join_as_personal(tr::now)); + } else if (peer->isMegagroup()) { + result->setCustomStatus(u"Anonymous admin"_q); + } else if (const auto channel = peer->asChannel()) { + result->setCustomStatus(tr::lng_chat_status_subscribers( + tr::now, + lt_count, + channel->membersCount())); + } + return result; +} + +void ListController::prepare() { + delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled); + for (const auto &peer : _list) { + auto row = createRow(peer); + const auto raw = row.get(); + delegate()->peerListAppendRow(std::move(row)); + if (peer == _selected) { + delegate()->peerListSetRowChecked(raw, true); + raw->finishCheckedAnimation(); + } + } + delegate()->peerListRefreshRows(); +} + +void ListController::rowClicked(not_null row) { + const auto peer = row->peer(); + if (peer == _selected) { + return; + } + const auto previous = delegate()->peerListFindRow(_selected->id.value); + Assert(previous != nullptr); + delegate()->peerListSetRowChecked(previous, false); + delegate()->peerListSetRowChecked(row, true); + _selected = peer; +} + +not_null ListController::selected() const { + return _selected; +} + +} // namespace + +void ChooseSendAsBox( + not_null box, + std::vector> list, + not_null chosen, + Fn)> done) { + Expects(ranges::contains(list, chosen)); + Expects(done != nullptr); + + box->setWidth(st::groupCallJoinAsWidth); + box->setTitle(rpl::single(u"Send message as..."_q)); + const auto &labelSt = st::confirmPhoneAboutLabel; + box->addRow(object_ptr( + box, + tr::lng_group_call_join_as_about(), + labelSt)); + + auto &lifetime = box->lifetime(); + const auto delegate = lifetime.make_state< + PeerListContentDelegateSimple + >(); + const auto controller = lifetime.make_state( + list, + chosen); + controller->setStyleOverrides( + &st::peerListJoinAsList, + nullptr); + const auto content = box->addRow( + object_ptr(box, controller), + style::margins()); + delegate->setContent(content); + controller->setDelegate(delegate); + box->addButton(tr::lng_settings_save(), [=] { + done(controller->selected()); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +void SetupSendAsButton( + not_null button, + not_null window) { + using namespace rpl::mappers; + button->setClickedCallback([=] { + const auto history = window->activeChatCurrent().history(); + if (!history) { + return; + } + const auto peer = history->peer; + const auto session = &peer->session(); + const auto &list = session->sendAsPeers().list(peer); + if (list.size() < 2) { + return; + } + const auto done = [=](not_null sendAs) { + session->sendAsPeers().saveChosen(peer, sendAs); + }; + window->show(Box( + Ui::ChooseSendAsBox, + list, + session->sendAsPeers().resolveChosen(peer), + done)); + }); + + auto userpic = window->activeChatValue( + ) | rpl::map([=](const Dialogs::Key &key) -> PeerData* { + if (const auto history = key.history()) { + return history->peer->isMegagroup() + ? history->peer.get() + : nullptr; + } + return nullptr; + }) | rpl::filter_nullptr( + ) | rpl::map([=](not_null peer) { + const auto channel = peer->asMegagroup(); + + auto updates = rpl::single( + rpl::empty_value() + ) | rpl::then(channel->session().sendAsPeers().updated( + ) | rpl::filter( + _1 == channel + ) | rpl::to_empty); + + return rpl::combine( + std::move(updates), + channel->adminRightsValue() + ) | rpl::map([=] { + return channel->session().sendAsPeers().resolveChosen(channel); + }) | rpl::distinct_until_changed( + ) | rpl::map([=](not_null chosen) { + return Data::PeerUserpicImageValue( + chosen, + st::sendAsButton.size * style::DevicePixelRatio()); + }) | rpl::flatten_latest(); + }) | rpl::flatten_latest(); + + std::move( + userpic + ) | rpl::start_with_next([=](QImage &&userpic) { + button->setUserpic(std::move(userpic)); + }, button->lifetime()); + +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.h b/Telegram/SourceFiles/ui/chat/choose_send_as.h new file mode 100644 index 000000000..eca82cc5b --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.h @@ -0,0 +1,33 @@ +/* +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 "base/object_ptr.h" +#include "ui/layers/generic_box.h" + +class PeerData; + +namespace Window { +class SessionController; +} // namespace Window + +namespace Ui { + +class SendAsButton; + +void ChooseSendAsBox( + not_null box, + std::vector> list, + not_null chosen, + Fn)> done); + +void SetupSendAsButton( + not_null button, + not_null window); + +} // namespace Ui From 773755d70e10491fdb674bef5eac678b130c787d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 19:24:13 +0400 Subject: [PATCH 036/180] Send as a channel in HistoryWidget. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/api/api_common.h | 9 ++- Telegram/SourceFiles/api/api_polls.cpp | 6 +- Telegram/SourceFiles/api/api_sending.cpp | 42 +++++++++--- Telegram/SourceFiles/apiwrap.cpp | 58 ++++++++++++---- .../SourceFiles/boxes/edit_caption_box.cpp | 3 +- .../boxes/peer_list_controllers.cpp | 2 +- .../boxes/peers/edit_peer_invite_link.cpp | 3 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 4 +- Telegram/SourceFiles/boxes/share_box.cpp | 4 +- .../SourceFiles/boxes/sticker_set_box.cpp | 2 +- .../calls/group/calls_group_settings.cpp | 4 +- .../chat_helpers/field_autocomplete.cpp | 2 +- .../SourceFiles/data/data_media_types.cpp | 3 +- .../data/data_scheduled_messages.cpp | 2 +- .../SourceFiles/history/history_message.cpp | 6 +- .../SourceFiles/history/history_widget.cpp | 68 +++++++++---------- Telegram/SourceFiles/history/history_widget.h | 3 + .../history_view_compose_controls.cpp | 10 ++- .../history/view/history_view_message.cpp | 2 +- .../view/history_view_replies_section.cpp | 59 +++++++--------- .../view/history_view_replies_section.h | 3 + .../view/history_view_scheduled_section.cpp | 53 +++++++-------- .../view/history_view_scheduled_section.h | 3 + Telegram/SourceFiles/mainwidget.cpp | 2 +- .../passport/passport_form_controller.cpp | 2 +- .../mac/touchbar/items/mac_scrubber_item.mm | 3 +- .../SourceFiles/storage/localimageloader.h | 2 +- Telegram/SourceFiles/ui/chat/chat.style | 4 +- .../SourceFiles/ui/chat/choose_send_as.cpp | 4 ++ .../window/notifications_manager.cpp | 2 +- .../SourceFiles/window/window_peer_menu.cpp | 5 +- 32 files changed, 221 insertions(+), 156 deletions(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 9c67ebcfb..bfd931857 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1085,6 +1085,8 @@ PRIVATE ui/chat/attach/attach_item_single_file_preview.h ui/chat/attach/attach_item_single_media_preview.cpp ui/chat/attach/attach_item_single_media_preview.h + ui/chat/choose_send_as.cpp + ui/chat/choose_send_as.h ui/chat/choose_theme_controller.cpp ui/chat/choose_theme_controller.h ui/effects/fireworks_animation.cpp diff --git a/Telegram/SourceFiles/api/api_common.h b/Telegram/SourceFiles/api/api_common.h index 112727cc5..6f5b15b35 100644 --- a/Telegram/SourceFiles/api/api_common.h +++ b/Telegram/SourceFiles/api/api_common.h @@ -12,6 +12,7 @@ class History; namespace Api { struct SendOptions { + PeerData *sendAs = nullptr; TimeId scheduled = 0; bool silent = false; bool handleSupportSwitch = false; @@ -25,7 +26,11 @@ enum class SendType { }; struct SendAction { - explicit SendAction(not_null history) : history(history) { + explicit SendAction( + not_null history, + SendOptions options = SendOptions()) + : history(history) + , options(options) { } not_null history; @@ -37,7 +42,7 @@ struct SendAction { }; struct MessageToSend { - explicit MessageToSend(not_null history) : action(history) { + explicit MessageToSend(SendAction action) : action(action) { } SendAction action; diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp index 65d00b58f..80c3c55ae 100644 --- a/Telegram/SourceFiles/api/api_polls.cpp +++ b/Telegram/SourceFiles/api/api_polls.cpp @@ -60,6 +60,10 @@ void Polls::create( if (action.options.scheduled) { sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date; } + const auto sendAs = action.options.sendAs; + if (sendAs) { + sendFlags |= MTPmessages_SendMedia::Flag::f_send_as; + } auto &histories = history->owner().histories(); const auto requestType = Data::Histories::RequestType::Send; histories.sendRequest(history, requestType, [=](Fn finish) { @@ -74,7 +78,7 @@ void Polls::create( MTPReplyMarkup(), MTPVector(), MTP_int(action.options.scheduled), - MTPInputPeer() // #TODO send_as + (sendAs ? sendAs->input : MTP_inputPeerEmpty()) )).done([=]( const MTPUpdates &result, const MTP::Response &response) mutable { diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 4afdc695a..bb7973423 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -90,8 +90,18 @@ void SendExistingMedia( if (silentPost) { sendFlags |= MTPmessages_SendMedia::Flag::f_silent; } - auto messageFromId = anonymousPost ? 0 : session->userPeerId(); - auto messagePostAuthor = peer->isBroadcast() ? session->user()->name : QString(); + const auto sendAs = message.action.options.sendAs; + const auto messageFromId = sendAs + ? sendAs->id + : anonymousPost + ? 0 + : session->userPeerId(); + if (sendAs) { + sendFlags |= MTPmessages_SendMedia::Flag::f_send_as; + } + const auto messagePostAuthor = peer->isBroadcast() + ? session->user()->name + : QString(); auto caption = TextWithEntities{ message.textWithTags.text, @@ -143,7 +153,7 @@ void SendExistingMedia( MTPReplyMarkup(), sentEntities, MTP_int(message.action.options.scheduled), - MTPInputPeer() // #TODO send_as + (sendAs ? sendAs->input : MTP_inputPeerEmpty()) )).done([=](const MTPUpdates &result) { api->applyUpdates(result, randomId); finish(); @@ -263,8 +273,18 @@ bool SendDice(MessageToSend &message) { if (silentPost) { sendFlags |= MTPmessages_SendMedia::Flag::f_silent; } - auto messageFromId = anonymousPost ? 0 : session->userPeerId(); - auto messagePostAuthor = peer->isBroadcast() ? session->user()->name : QString(); + const auto sendAs = message.action.options.sendAs; + const auto messageFromId = sendAs + ? sendAs->id + : anonymousPost + ? 0 + : session->userPeerId(); + if (sendAs) { + sendFlags |= MTPmessages_SendMedia::Flag::f_send_as; + } + const auto messagePostAuthor = peer->isBroadcast() + ? session->user()->name + : QString(); const auto replyTo = message.action.replyTo; if (message.action.options.scheduled) { @@ -299,7 +319,7 @@ bool SendDice(MessageToSend &message) { MTPReplyMarkup(), MTP_vector(), MTP_int(message.action.options.scheduled), - MTPInputPeer() // #TODO send_as + (sendAs ? sendAs->input : MTP_inputPeerEmpty()) )).done([=](const MTPUpdates &result) { api->applyUpdates(result, randomId); finish(); @@ -352,8 +372,7 @@ void SendConfirmedFile( const auto history = session->data().history(file->to.peer); const auto peer = history->peer; - auto action = SendAction(history); - action.options = file->to.options; + auto action = SendAction(history, file->to.options); action.clearDraft = false; action.replyTo = file->to.replyTo; action.generateLocal = true; @@ -392,7 +411,12 @@ void SendConfirmedFile( } } - const auto messageFromId = anonymousPost ? 0 : session->userPeerId(); + const auto messageFromId = + file->to.options.sendAs + ? file->to.options.sendAs->id + : anonymousPost + ? PeerId() + : session->userPeerId(); const auto messagePostAuthor = peer->isBroadcast() ? session->user()->name : QString(); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index b8b6bfca9..750c522aa 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3676,6 +3676,7 @@ void ApiWrap::forwardMessages( } const auto anonymousPost = peer->amAnonymous(); const auto silentPost = ShouldSendSilent(peer, action.options); + const auto sendAs = action.options.sendAs; auto flags = MessageFlags(); auto sendFlags = MTPmessages_ForwardMessages::Flags(0); @@ -3693,6 +3694,9 @@ void ApiWrap::forwardMessages( if (draft.options == Data::ForwardOptions::NoNamesAndCaptions) { sendFlags |= MTPmessages_ForwardMessages::Flag::f_drop_media_captions; } + if (sendAs) { + sendFlags |= MTPmessages_ForwardMessages::Flag::f_send_as; + } auto forwardFrom = draft.items.front()->history()->peer; auto ids = QVector(); @@ -3713,7 +3717,7 @@ void ApiWrap::forwardMessages( MTP_vector(randomIds), peer->input, MTP_int(action.options.scheduled), - MTPInputPeer() // #TODO send_as + (sendAs ? sendAs->input : MTP_inputPeerEmpty()) )).done([=](const MTPUpdates &result) { applyUpdates(result); if (shared && !--shared->requestsLeft) { @@ -3749,7 +3753,9 @@ void ApiWrap::forwardMessages( peerToChannel(peer->id), _session->data().nextLocalMessageId()); const auto self = _session->user(); - const auto messageFromId = anonymousPost + const auto messageFromId = sendAs + ? sendAs->id + : anonymousPost ? PeerId(0) : self->id; const auto messagePostAuthor = peer->isBroadcast() @@ -3830,7 +3836,11 @@ void ApiWrap::sendSharedContact( if (action.options.scheduled) { flags |= MessageFlag::IsOrWasScheduled; } - const auto messageFromId = anonymousPost ? 0 : _session->userPeerId(); + const auto messageFromId = action.options.sendAs + ? action.options.sendAs->id + : anonymousPost + ? PeerId() + : _session->userPeerId(); const auto messagePostAuthor = peer->isBroadcast() ? _session->user()->name : QString(); @@ -3910,9 +3920,8 @@ void ApiWrap::sendFiles( const SendAction &action) { const auto haveCaption = !caption.text.isEmpty(); if (haveCaption && !list.canAddCaption(album != nullptr)) { - auto message = MessageToSend(action.history); + auto message = MessageToSend(action); message.textWithTags = base::take(caption); - message.action = action; message.action.clearDraft = false; sendMessage(std::move(message)); } @@ -4086,8 +4095,16 @@ void ApiWrap::sendMessage(MessageToSend &&message) { history->clearCloudDraft(); history->startSavingCloudDraft(); } - auto messageFromId = anonymousPost ? 0 : _session->userPeerId(); - auto messagePostAuthor = peer->isBroadcast() + const auto sendAs = action.options.sendAs; + const auto messageFromId = sendAs + ? sendAs->id + : anonymousPost + ? PeerId() + : _session->userPeerId(); + if (sendAs) { + sendFlags |= MTPmessages_SendMessage::Flag::f_send_as; + } + const auto messagePostAuthor = peer->isBroadcast() ? _session->user()->name : QString(); if (action.options.scheduled) { @@ -4116,7 +4133,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { MTPReplyMarkup(), sentEntities, MTP_int(action.options.scheduled), - MTPInputPeer() // #TODO send_as + (sendAs ? sendAs->input : MTP_inputPeerEmpty()) )).done([=]( const MTPUpdates &result, const MTP::Response &response) { @@ -4160,7 +4177,8 @@ void ApiWrap::sendBotStart(not_null bot, PeerData *chat) { auto &info = bot->botInfo; auto &token = chat ? info->startGroupToken : info->startToken; if (token.isEmpty()) { - auto message = ApiWrap::MessageToSend(_session->data().history(bot)); + auto message = MessageToSend( + Api::SendAction(_session->data().history(bot))); message.textWithTags = { qsl("/start"), TextWithTags::Tags() }; sendMessage(std::move(message)); return; @@ -4213,7 +4231,14 @@ void ApiWrap::sendInlineResult( sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_schedule_date; } - const auto messageFromId = anonymousPost ? 0 : _session->userPeerId(); + const auto sendAs = action.options.sendAs; + const auto messageFromId = sendAs + ? sendAs->id + : anonymousPost ? PeerId() + : _session->userPeerId(); + if (sendAs) { + sendFlags |= MTPmessages_SendInlineBotResult::Flag::f_send_as; + } const auto messagePostAuthor = peer->isBroadcast() ? _session->user()->name : QString(); @@ -4244,7 +4269,7 @@ void ApiWrap::sendInlineResult( MTP_long(data->getQueryId()), MTP_string(data->getId()), MTP_int(action.options.scheduled), - MTPInputPeer() // #TODO send_as + (sendAs ? sendAs->input : MTP_inputPeerEmpty()) )).done([=]( const MTPUpdates &result, const MTP::Response &response) { @@ -4381,6 +4406,9 @@ void ApiWrap::sendMediaWithRandomId( : MTPmessages_SendMedia::Flag(0)) | (options.scheduled ? MTPmessages_SendMedia::Flag::f_schedule_date + : MTPmessages_SendMedia::Flag(0)) + | (options.sendAs + ? MTPmessages_SendMedia::Flag::f_send_as : MTPmessages_SendMedia::Flag(0)); auto &histories = history->owner().histories(); @@ -4398,7 +4426,7 @@ void ApiWrap::sendMediaWithRandomId( MTPReplyMarkup(), sentEntities, MTP_int(options.scheduled), - MTPInputPeer() // #TODO send_as + (options.sendAs ? options.sendAs->input : MTP_inputPeerEmpty()) )).done([=](const MTPUpdates &result) { applyUpdates(result); finish(); @@ -4481,6 +4509,7 @@ void ApiWrap::sendAlbumIfReady(not_null album) { } const auto history = sample->history(); const auto replyTo = sample->replyToId(); + const auto sendAs = album->options.sendAs; const auto flags = MTPmessages_SendMultiMedia::Flags(0) | (replyTo ? MTPmessages_SendMultiMedia::Flag::f_reply_to_msg_id @@ -4490,6 +4519,9 @@ void ApiWrap::sendAlbumIfReady(not_null album) { : MTPmessages_SendMultiMedia::Flag(0)) | (album->options.scheduled ? MTPmessages_SendMultiMedia::Flag::f_schedule_date + : MTPmessages_SendMultiMedia::Flag(0)) + | (sendAs + ? MTPmessages_SendMultiMedia::Flag::f_send_as : MTPmessages_SendMultiMedia::Flag(0)); auto &histories = history->owner().histories(); const auto requestType = Data::Histories::RequestType::Send; @@ -4501,7 +4533,7 @@ void ApiWrap::sendAlbumIfReady(not_null album) { MTP_int(replyTo), MTP_vector(medias), MTP_int(album->options.scheduled), - MTPInputPeer() // #TODO send_as + (sendAs ? sendAs->input : MTP_inputPeerEmpty()) )).done([=](const MTPUpdates &result) { _sendingAlbums.remove(groupId); applyUpdates(result); diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 09c9ccdd0..e5a438b9d 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -670,8 +670,7 @@ void EditCaptionBox::save() { options.scheduled = item->isScheduled() ? item->date() : 0; if (!_preparedList.files.empty()) { - auto action = Api::SendAction(item->history()); - action.options = options; + auto action = Api::SendAction(item->history(), options); action.replaceMediaOf = item->fullId().msg; Storage::ApplyModifications(_preparedList); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 455c88543..39aacbd06 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -51,7 +51,7 @@ void ShareBotGame(not_null bot, not_null chat) { MTPReplyMarkup(), MTPVector(), MTP_int(0), // schedule_date - MTPInputPeer() // #TODO send_as + MTPInputPeer() // send_as )).done([=](const MTPUpdates &result) { api->applyUpdates(result, randomId); finish(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 9c4c74720..61b6af100 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -1144,9 +1144,8 @@ void ShareInviteLinkBox(not_null peer, const QString &link) { auto &api = peer->session().api(); for (const auto peer : result) { const auto history = owner->history(peer); - auto message = ApiWrap::MessageToSend(history); + auto message = Api::MessageToSend(Api::SendAction(history, options)); message.textWithTags = comment; - message.action.options = options; message.action.clearDraft = false; api.sendMessage(std::move(message)); } diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index d59f9559d..b85eb88d4 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -1026,9 +1026,7 @@ void SendFilesBox::send( } void SendFilesBox::sendSilent() { - auto options = Api::SendOptions(); - options.silent = true; - send(options); + send({ .silent = true }); } void SendFilesBox::sendScheduled() { diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 138a2575d..802a943c0 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -496,9 +496,7 @@ void ShareBox::submit(Api::SendOptions options) { } void ShareBox::submitSilent() { - auto options = Api::SendOptions(); - options.silent = true; - submit(options); + submit({ .silent = true }); } void ShareBox::submitScheduled() { diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 003257f91..7aacd11fd 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -625,7 +625,7 @@ void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) { if (index < 0 || index >= _pack.size() || isMasksSet()) { return; } - send(_pack[index], Api::SendOptions()); + send(_pack[index], {}); } void StickerSetBox::Inner::send( diff --git a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp index ce9a89669..a7a011b6c 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp @@ -175,9 +175,9 @@ object_ptr ShareInviteLinkBox( auto &api = peer->session().api(); for (const auto peer : result) { const auto history = owner->history(peer); - auto message = ApiWrap::MessageToSend(history); + auto message = Api::MessageToSend( + Api::SendAction(history, options)); message.textWithTags = comment; - message.action.options = options; message.action.clearDraft = false; api.sendMessage(std::move(message)); } diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 45b55a9f4..48ac4888a 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -66,7 +66,7 @@ public: bool chooseAtIndex( FieldAutocomplete::ChooseMethod method, int index, - Api::SendOptions options = Api::SendOptions()) const; + Api::SendOptions options = {}) const; void setRecentInlineBotsInRows(int32 bots); void setSendMenuType(Fn &&callback); diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 91eaba4b1..ae4d3c25a 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -1683,7 +1683,8 @@ ClickHandlerPtr MediaDice::MakeHandler( const ClickHandlerPtr &handler, Qt::MouseButton button) { if (button == Qt::LeftButton && !ShownToast.empty()) { - auto message = Api::MessageToSend(history); + auto message = Api::MessageToSend( + Api::SendAction(history)); message.action.clearDraft = false; message.textWithTags.text = emoji; diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index 374b4cc30..f90a0da49 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -195,7 +195,7 @@ void ScheduledMessages::sendNowSimpleMessage( MTP_message( MTP_flags(flags), update.vid(), - peerToMTP(_session->userPeerId()), + peerToMTP(local->from()->id), peerToMTP(history->peer->id), MTPMessageFwdHeader(), MTPlong(), // via_bot_id diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index ddde57fce..2fccf51b7 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -293,9 +293,9 @@ void FastShareMessage(not_null item) { for (const auto peer : result) { const auto history = owner->history(peer); if (!comment.text.isEmpty()) { - auto message = ApiWrap::MessageToSend(history); + auto message = Api::MessageToSend( + Api::SendAction(history, options)); message.textWithTags = comment; - message.action.options = options; message.action.clearDraft = false; api.sendMessage(std::move(message)); } @@ -312,7 +312,7 @@ void FastShareMessage(not_null item) { MTP_vector(generateRandom()), peer->input, MTP_int(options.scheduled), - MTPInputPeer() // #TODO send_as + MTP_inputPeerEmpty() // send_as )).done([=](const MTPUpdates &updates, mtpRequestId requestId) { history->session().api().applyUpdates(updates); data->requests.remove(requestId); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9bcaa4d54..9caa842c2 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -890,9 +890,7 @@ void HistoryWidget::initVoiceRecordBar() { return; } - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - action.options = data.options; + auto action = prepareSendAction(data.options); session().api().sendVoiceMessage( data.bytes, data.waveform, @@ -1025,7 +1023,9 @@ void HistoryWidget::supportShareContact(Support::Contact contact) { if (!history) { return; } - auto options = Api::SendOptions(); + auto options = Api::SendOptions{ + .sendAs = prepareSendAction({}).options.sendAs, + }; auto action = Api::SendAction(history); send(options); options.handleSupportSwitch = Support::HandleSwitch(modifiers); @@ -1298,7 +1298,7 @@ void HistoryWidget::insertHashtagOrBotCommand( // Send bot command at once, if it was not inserted by pressing Tab. if (str.at(0) == '/' && method != FieldAutocomplete::ChooseMethod::ByTab) { sendBotCommand({ _peer, str, FullMsgId(), replyToId() }); - session().api().finishForwarding(Api::SendAction(_history)); + session().api().finishForwarding(prepareSendAction({})); setFieldText(_field->getTextWithTagsPart(_field->textCursor().position())); } else { _field->insertTag(str); @@ -3405,10 +3405,12 @@ void HistoryWidget::saveEditMsg() { })(); }; + auto options = Api::SendOptions(); + options.removeWebPageId = (webPageId == CancelledWebPageId); _saveEditMsgRequestId = Api::EditTextMessage( item, sending, - { .removeWebPageId = (webPageId == CancelledWebPageId) }, + options, done, fail); } @@ -3448,6 +3450,17 @@ void HistoryWidget::hideSelectorControlsAnimated() { } } +Api::SendAction HistoryWidget::prepareSendAction( + Api::SendOptions options) const { + auto result = Api::SendAction(_history, options); + result.replyTo = replyToId(); + result.options.sendAs = _sendAs + ? _history->session().sendAsPeers().resolveChosen( + _history->peer).get() + : nullptr; + return result; +} + void HistoryWidget::send(Api::SendOptions options) { if (!_history) { return; @@ -3469,10 +3482,8 @@ void HistoryWidget::send(Api::SendOptions options) { ? _previewData->id : WebPageId(0)); - auto message = ApiWrap::MessageToSend(_history); + auto message = ApiWrap::MessageToSend(prepareSendAction(options)); message.textWithTags = _field->getTextWithAppliedMarkdown(); - message.action.options = options; - message.action.replyTo = replyToId(); message.webPageId = webPageId; if (_canSendMessages) { @@ -3512,15 +3523,11 @@ void HistoryWidget::send(Api::SendOptions options) { } void HistoryWidget::sendWithModifiers(Qt::KeyboardModifiers modifiers) { - auto options = Api::SendOptions(); - options.handleSupportSwitch = Support::HandleSwitch(modifiers); - send(options); + send({ .handleSupportSwitch = Support::HandleSwitch(modifiers) }); } void HistoryWidget::sendSilent() { - auto options = Api::SendOptions(); - options.silent = true; - send(options); + send({ .silent = true }); } void HistoryWidget::sendScheduled() { @@ -3894,7 +3901,7 @@ void HistoryWidget::sendBotCommand(const Bot::SendCommandRequest &request) { ? request.command : Bot::WrapCommandInChat(_peer, request.command, request.context); - auto message = ApiWrap::MessageToSend(_history); + auto message = Api::MessageToSend(prepareSendAction({})); message.textWithTags = { toSend, TextWithTags::Tags() }; message.action.replyTo = request.replyTo ? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/) @@ -4675,15 +4682,12 @@ void HistoryWidget::sendingFilesConfirmed( const auto type = way.sendImagesAsPhotos() ? SendMediaType::Photo : SendMediaType::File; - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - action.options = options; + auto action = prepareSendAction(options); action.clearDraft = false; if ((groups.size() != 1 || !groups.front().sentWithCaption()) && !caption.text.isEmpty()) { - auto message = Api::MessageToSend(_history); + auto message = Api::MessageToSend(action); message.textWithTags = base::take(caption); - message.action = action; session().api().sendMessage(std::move(message)); } for (auto &group : groups) { @@ -4773,9 +4777,7 @@ void HistoryWidget::uploadFile( SendMediaType type) { if (!canWriteMessage()) return; - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - session().api().sendFile(fileContent, type, action); + session().api().sendFile(fileContent, type, prepareSendAction({})); } void HistoryWidget::handleHistoryChange(not_null history) { @@ -5791,9 +5793,7 @@ void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) { return; } - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - action.options = std::move(result.options); + auto action = prepareSendAction(result.options); action.generateLocal = true; session().api().sendInlineResult(result.bot, result.result, action); @@ -6175,10 +6175,9 @@ bool HistoryWidget::sendExistingDocument( return false; } - auto message = Api::MessageToSend(_history); - message.action.options = std::move(options); - message.action.replyTo = replyToId(); - Api::SendExistingDocument(std::move(message), document); + Api::SendExistingDocument( + Api::MessageToSend(prepareSendAction(options)), + document); if (_fieldAutocomplete->stickersShown()) { clearFieldText(); @@ -6211,10 +6210,9 @@ bool HistoryWidget::sendExistingPhoto( return false; } - auto message = Api::MessageToSend(_history); - message.action.replyTo = replyToId(); - message.action.options = std::move(options); - Api::SendExistingPhoto(std::move(message), photo); + Api::SendExistingPhoto( + Api::MessageToSend(prepareSendAction(options)), + photo); hideSelectorControlsAnimated(); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 3406eda25..30399fe9d 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -42,6 +42,7 @@ enum class Type; namespace Api { struct SendOptions; +struct SendAction; } // namespace Api namespace InlineBots { @@ -372,6 +373,8 @@ private: void requestMessageData(MsgId msgId); void messageDataReceived(ChannelData *channel, MsgId msgId); + [[nodiscard]] Api::SendAction prepareSendAction( + Api::SendOptions options) const; void send(Api::SendOptions options); void sendWithModifiers(Qt::KeyboardModifiers modifiers); void sendSilent(); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index f6b7600ae..c86ba7b1f 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -570,12 +570,10 @@ MessageToEdit FieldHeader::queryToEdit() { return {}; } return { - item->fullId(), - { - item->isScheduled() ? item->date() : 0, - false, - false, - !hasPreview(), + .fullId = item->fullId(), + .options = { + .scheduled = item->isScheduled() ? item->date() : 0, + .removeWebPageId = !hasPreview(), }, }; } diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 364554177..d6ebd1d6c 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2062,7 +2062,7 @@ bool Message::hasFromName() const { case Context::Replies: { const auto item = message(); const auto peer = item->history()->peer; - if (hasOutLayout() && !item->from()->isMegagroup()) { + if (hasOutLayout() && !item->from()->isChannel()) { return false; } else if (!peer->isUser()) { return true; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 7071d6f24..02474893a 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -735,19 +735,15 @@ void RepliesWidget::sendingFilesConfirmed( std::move(list), way, _history->peer->slowmodeApplied()); - const auto replyTo = replyToId(); const auto type = way.sendImagesAsPhotos() ? SendMediaType::Photo : SendMediaType::File; - auto action = Api::SendAction(_history); - action.replyTo = replyTo ? replyTo : _rootId; - action.options = options; + auto action = prepareSendAction(options); action.clearDraft = false; if ((groups.size() != 1 || !groups.front().sentWithCaption()) && !caption.text.isEmpty()) { - auto message = Api::MessageToSend(_history); + auto message = Api::MessageToSend(action); message.textWithTags = base::take(caption); - message.action = action; session().api().sendMessage(std::move(message)); } for (auto &group : groups) { @@ -761,7 +757,7 @@ void RepliesWidget::sendingFilesConfirmed( album, action); } - if (_composeControls->replyingToMessage().msg == replyTo) { + if (_composeControls->replyingToMessage().msg == action.replyTo) { _composeControls->cancelReplyMessage(); refreshTopBarActiveChat(); } @@ -869,9 +865,7 @@ void RepliesWidget::uploadFile( const QByteArray &fileContent, SendMediaType type) { // #TODO replies schedule - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - session().api().sendFile(fileContent, type, action); + session().api().sendFile(fileContent, type, prepareSendAction({})); } bool RepliesWidget::showSendingFilesError( @@ -918,11 +912,18 @@ bool RepliesWidget::showSendingFilesError( return true; } +Api::SendAction RepliesWidget::prepareSendAction( + Api::SendOptions options) const { + auto result = Api::SendAction(_history, options); + result.replyTo = replyToId(); + return result; +} + void RepliesWidget::send() { if (_composeControls->getTextWithAppliedMarkdown().text.isEmpty()) { return; } - send(Api::SendOptions()); + send({}); // #TODO replies schedule //const auto callback = [=](Api::SendOptions options) { send(options); }; //Ui::show( @@ -931,9 +932,7 @@ void RepliesWidget::send() { } void RepliesWidget::sendVoice(ComposeControls::VoiceToSend &&data) { - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - action.options = data.options; + auto action = prepareSendAction(data.options); session().api().sendVoiceMessage( data.bytes, data.waveform, @@ -952,10 +951,8 @@ void RepliesWidget::send(Api::SendOptions options) { const auto webPageId = _composeControls->webPageId(); - auto message = ApiWrap::MessageToSend(_history); + auto message = ApiWrap::MessageToSend(prepareSendAction(options)); message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); - message.action.options = options; - message.action.replyTo = replyToId(); message.webPageId = webPageId; //const auto error = GetErrorTextForSending( @@ -1063,7 +1060,7 @@ void RepliesWidget::edit( void RepliesWidget::sendExistingDocument( not_null document) { - sendExistingDocument(document, Api::SendOptions()); + sendExistingDocument(document, {}); // #TODO replies schedule //const auto callback = [=](Api::SendOptions options) { // sendExistingDocument(document, options); @@ -1088,10 +1085,9 @@ bool RepliesWidget::sendExistingDocument( return false; } - auto message = Api::MessageToSend(_history); - message.action.replyTo = replyToId(); - message.action.options = options; - Api::SendExistingDocument(std::move(message), document); + Api::SendExistingDocument( + Api::MessageToSend(prepareSendAction(options)), + document); _composeControls->cancelReplyMessage(); finishSending(); @@ -1099,7 +1095,7 @@ bool RepliesWidget::sendExistingDocument( } void RepliesWidget::sendExistingPhoto(not_null photo) { - sendExistingPhoto(photo, Api::SendOptions()); + sendExistingPhoto(photo, {}); // #TODO replies schedule //const auto callback = [=](Api::SendOptions options) { // sendExistingPhoto(photo, options); @@ -1124,10 +1120,9 @@ bool RepliesWidget::sendExistingPhoto( return false; } - auto message = Api::MessageToSend(_history); - message.action.replyTo = replyToId(); - message.action.options = options; - Api::SendExistingPhoto(std::move(message), photo); + Api::SendExistingPhoto( + Api::MessageToSend(prepareSendAction(options)), + photo); _composeControls->cancelReplyMessage(); finishSending(); @@ -1142,7 +1137,7 @@ void RepliesWidget::sendInlineResult( controller()->show(Box(errorText)); return; } - sendInlineResult(result, bot, Api::SendOptions()); + sendInlineResult(result, bot, {}); //const auto callback = [=](Api::SendOptions options) { // sendInlineResult(result, bot, options); //}; @@ -1155,9 +1150,7 @@ void RepliesWidget::sendInlineResult( not_null result, not_null bot, Api::SendOptions options) { - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - action.options = options; + auto action = prepareSendAction(options); action.generateLocal = true; session().api().sendInlineResult(bot, result, action); @@ -1912,9 +1905,9 @@ void RepliesWidget::listSendBotCommand( _history->peer, command, context); - auto message = ApiWrap::MessageToSend(_history); + auto message = ApiWrap::MessageToSend( + prepareSendAction({})); message.textWithTags = { text }; - message.action.replyTo = replyToId(); session().api().sendMessage(std::move(message)); finishSending(); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index c7a83473e..f2700f364 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -23,6 +23,7 @@ enum class Type; namespace Api { struct SendOptions; +struct SendAction; } // namespace Api namespace Storage { @@ -190,6 +191,8 @@ private: void clearSelected(); void setPinnedVisibility(bool shown); + [[nodiscard]] Api::SendAction prepareSendAction( + Api::SendOptions options) const; void send(); void send(Api::SendOptions options); void sendVoice(Controls::VoiceToSend &&data); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 1cc373f0e..6230bd041 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -429,14 +429,12 @@ void ScheduledWidget::sendingFilesConfirmed( const auto type = way.sendImagesAsPhotos() ? SendMediaType::Photo : SendMediaType::File; - auto action = Api::SendAction(_history); - action.options = options; + auto action = prepareSendAction(options); action.clearDraft = false; if ((groups.size() != 1 || !groups.front().sentWithCaption()) && !caption.text.isEmpty()) { - auto message = Api::MessageToSend(_history); + auto message = Api::MessageToSend(action); message.textWithTags = base::take(caption); - message.action = action; session().api().sendMessage(std::move(message)); } for (auto &group : groups) { @@ -473,10 +471,10 @@ void ScheduledWidget::uploadFile( const QByteArray &fileContent, SendMediaType type) { const auto callback = [=](Api::SendOptions options) { - auto action = Api::SendAction(_history); - //action.replyTo = replyToId(); - action.options = options; - session().api().sendFile(fileContent, type, action); + session().api().sendFile( + fileContent, + type, + prepareSendAction(options)); }; controller()->show( PrepareScheduleBox(this, sendMenuType(), callback), @@ -518,6 +516,12 @@ bool ScheduledWidget::showSendingFilesError( return true; } +Api::SendAction ScheduledWidget::prepareSendAction( + Api::SendOptions options) const { + auto result = Api::SendAction(_history, options); + return result; +} + void ScheduledWidget::send() { if (_composeControls->getTextWithAppliedMarkdown().text.isEmpty()) { return; @@ -531,10 +535,8 @@ void ScheduledWidget::send() { void ScheduledWidget::send(Api::SendOptions options) { const auto webPageId = _composeControls->webPageId(); - auto message = ApiWrap::MessageToSend(_history); + auto message = ApiWrap::MessageToSend(prepareSendAction(options)); message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); - message.action.options = options; - //message.action.replyTo = replyToId(); message.webPageId = webPageId; //const auto error = GetErrorTextForSending( @@ -578,9 +580,11 @@ void ScheduledWidget::sendVoice( VoiceWaveform waveform, int duration, Api::SendOptions options) { - auto action = Api::SendAction(_history); - action.options = options; - session().api().sendVoiceMessage(bytes, waveform, duration, action); + session().api().sendVoiceMessage( + bytes, + waveform, + duration, + prepareSendAction(options)); _composeControls->clearListenState(); } @@ -683,10 +687,9 @@ bool ScheduledWidget::sendExistingDocument( return false; } - auto message = Api::MessageToSend(_history); - //message.action.replyTo = replyToId(); - message.action.options = options; - Api::SendExistingDocument(std::move(message), document); + Api::SendExistingDocument( + Api::MessageToSend(prepareSendAction(options)), + document); _composeControls->hidePanelsAnimated(); _composeControls->focus(); @@ -715,10 +718,9 @@ bool ScheduledWidget::sendExistingPhoto( return false; } - auto message = Api::MessageToSend(_history); - //message.action.replyTo = replyToId(); - message.action.options = options; - Api::SendExistingPhoto(std::move(message), photo); + Api::SendExistingPhoto( + Api::MessageToSend(prepareSendAction(options)), + photo); _composeControls->hidePanelsAnimated(); _composeControls->focus(); @@ -745,9 +747,7 @@ void ScheduledWidget::sendInlineResult( not_null result, not_null bot, Api::SendOptions options) { - auto action = Api::SendAction(_history); - //action.replyTo = replyToId(); - action.options = options; + auto action = prepareSendAction(options); action.generateLocal = true; session().api().sendInlineResult(bot, result, action); @@ -1213,9 +1213,8 @@ void ScheduledWidget::listSendBotCommand( _history->peer, command, context); - auto message = ApiWrap::MessageToSend(_history); + auto message = ApiWrap::MessageToSend(prepareSendAction(options)); message.textWithTags = { text }; - message.action.options = options; session().api().sendMessage(std::move(message)); }; controller()->show( diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 47a805c40..2352eb0c1 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -22,6 +22,7 @@ enum class Type; namespace Api { struct SendOptions; +struct SendAction; } // namespace Api namespace Ui { @@ -155,6 +156,8 @@ private: void confirmDeleteSelected(); void clearSelected(); + [[nodiscard]] Api::SendAction prepareSendAction( + Api::SendOptions options) const; void send(); void send(Api::SendOptions options); void sendVoice(QByteArray bytes, VoiceWaveform waveform, int duration); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 5de7f7355..0becb6283 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1076,7 +1076,7 @@ SendMenu::Type MainWidget::sendMenuType() const { } bool MainWidget::sendExistingDocument(not_null document) { - return sendExistingDocument(document, Api::SendOptions()); + return sendExistingDocument(document, {}); } bool MainWidget::sendExistingDocument( diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index aa54ed1df..7680cd0ec 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -1581,7 +1581,7 @@ void FormController::uploadEncryptedFile( auto prepared = std::make_shared( TaskId(), file.uploadData->fileId, - FileLoadTo(PeerId(0), Api::SendOptions(), MsgId(0), MsgId(0)), + FileLoadTo(PeerId(), Api::SendOptions(), MsgId(), MsgId()), TextWithTags(), std::shared_ptr(nullptr)); prepared->type = SendMediaType::Secure; diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm index 36be91b02..8bdbdff6d 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm @@ -466,7 +466,8 @@ void AppendEmojiPacks( return true; } Api::SendExistingDocument( - Api::MessageToSend(ActiveChat(_controller).history()), + Api::MessageToSend( + Api::SendAction(ActiveChat(_controller).history())), document); return true; } else if (emoji) { diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index e2c44da02..6bb5ba88e 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -193,7 +193,7 @@ struct SendingAlbum { struct FileLoadTo { FileLoadTo( - const PeerId &peer, + PeerId peer, Api::SendOptions options, MsgId replyTo, MsgId replaceMediaOf) diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 81b45668b..d231ba3e2 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -931,11 +931,11 @@ SendAsButton { sendAsButton: SendAsButton { width: 44px; height: 46px; - size: 32px; + size: 28px; activeBg: activeButtonBg; activeFg: activeButtonFg; cross: CrossAnimation { - size: 32px; + size: 28px; skip: 10px; stroke: 2px; minScale: 0.3; diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp index 1a0b9681d..16ff42119 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -137,7 +137,11 @@ void ChooseSendAsBox( delegate->setContent(content); controller->setDelegate(delegate); box->addButton(tr::lng_settings_save(), [=] { + const auto weak = MakeWeak(box); done(controller->selected()); + if (weak) { + box->closeBox(); + } }); box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 04af477ed..fcb8876ab 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -706,7 +706,7 @@ void Manager::notificationReplied( } const auto history = session->data().history(id.full.peerId); - auto message = Api::MessageToSend(history); + auto message = Api::MessageToSend(Api::SendAction(history)); message.textWithTags = reply; message.action.replyTo = (id.msgId > 0 && !history->peer->isUser()) ? id.msgId diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index fb3faa80c..2abdd59cf 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -821,9 +821,10 @@ void PeerMenuCreatePoll( if (std::exchange(*lock, true)) { return; } - auto action = Api::SendAction(peer->owner().history(peer)); + auto action = Api::SendAction( + peer->owner().history(peer), + result.options); action.clearDraft = false; - action.options = result.options; action.replyTo = replyToId; if (const auto localDraft = action.history->localDraft()) { action.clearDraft = localDraft->textWithTags.text.isEmpty(); From ad52c0cb16265c91d06d93e329da3b0165a1dd57 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 20:42:35 +0400 Subject: [PATCH 037/180] Single-click change Send As. --- .../SourceFiles/ui/chat/choose_send_as.cpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp index 16ff42119..62b7af1f3 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -34,13 +34,14 @@ public: void prepare() override; void rowClicked(not_null row) override; - [[nodiscard]] not_null selected() const; + [[nodiscard]] rpl::producer> clicked() const; private: std::unique_ptr createRow(not_null peer); std::vector> _list; not_null _selected; + rpl::event_stream> _clicked; }; @@ -92,15 +93,11 @@ void ListController::rowClicked(not_null row) { if (peer == _selected) { return; } - const auto previous = delegate()->peerListFindRow(_selected->id.value); - Assert(previous != nullptr); - delegate()->peerListSetRowChecked(previous, false); - delegate()->peerListSetRowChecked(row, true); - _selected = peer; + _clicked.fire_copy(peer); } -not_null ListController::selected() const { - return _selected; +rpl::producer> ListController::clicked() const { + return _clicked.events(); } } // namespace @@ -131,19 +128,22 @@ void ChooseSendAsBox( controller->setStyleOverrides( &st::peerListJoinAsList, nullptr); + + controller->clicked( + ) | rpl::start_with_next([=](not_null peer) { + const auto weak = MakeWeak(box); + done(peer); + if (weak) { + box->closeBox(); + } + }, box->lifetime()); + const auto content = box->addRow( object_ptr(box, controller), style::margins()); delegate->setContent(content); controller->setDelegate(delegate); - box->addButton(tr::lng_settings_save(), [=] { - const auto weak = MakeWeak(box); - done(controller->selected()); - if (weak) { - box->closeBox(); - } - }); - box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + box->addButton(tr::lng_box_done(), [=] { box->closeBox(); }); } void SetupSendAsButton( From 8c824edaa58ab0347866ea636fbffd34211f3008 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 21:04:42 +0400 Subject: [PATCH 038/180] Choose send as in scheduled / discussions. --- .../SourceFiles/history/history_widget.cpp | 5 +-- .../history_view_compose_controls.cpp | 27 +++++++++++++++- .../controls/history_view_compose_controls.h | 3 ++ .../view/history_view_replies_section.cpp | 1 + .../view/history_view_scheduled_section.cpp | 1 + .../main/session/send_as_peers.cpp | 5 +++ .../SourceFiles/main/session/send_as_peers.h | 1 + .../SourceFiles/ui/chat/choose_send_as.cpp | 32 +++++++++++-------- Telegram/SourceFiles/ui/chat/choose_send_as.h | 5 +++ 9 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9caa842c2..2cbbf128c 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2387,10 +2387,7 @@ void HistoryWidget::setupSendAsToggle() { void HistoryWidget::refreshSendAsToggle() { Expects(_peer != nullptr); - session().sendAsPeers().refresh(_peer); - const auto &list = session().sendAsPeers().list(_peer); - const auto has = _peer->canWrite() && (list.size() > 1); - if (!has) { + if (!session().sendAsPeers().shouldChoose(_peer)) { _sendAs.destroy(); return; } else if (_sendAs) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index c86ba7b1f..7068e2b8c 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "inline_bots/inline_bot_result.h" #include "lang/lang_keys.h" #include "main/main_session.h" +#include "main/session/send_as_peers.h" #include "media/audio/media_audio_capture.h" #include "media/audio/media_audio.h" #include "styles/style_chat.h" @@ -50,6 +51,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/format_values.h" #include "ui/controls/emoji_button.h" #include "ui/controls/send_button.h" +#include "ui/controls/send_as_button.h" +#include "ui/chat/choose_send_as.h" #include "ui/special_buttons.h" #include "window/window_adaptive.h" #include "window/window_session_controller.h" @@ -675,6 +678,17 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) { _wrap.get(), peer->asChannel()); } + if (!session().sendAsPeers().shouldChoose(peer)) { + _sendAs = nullptr; + } else if (!_sendAs) { + _sendAs = std::make_unique( + _wrap.get(), + st::sendAsButton); + Ui::SetupSendAsButton( + _sendAs.get(), + rpl::single(peer.get()), + _window); + } session().local().readDraftsWithCursors(_history); applyDraft(); } @@ -686,6 +700,12 @@ void ComposeControls::setCurrentDialogsEntryState(Dialogs::EntryState state) { } } +PeerData *ComposeControls::sendAsPeer() const { + return (_sendAs && _history) + ? session().sendAsPeers().resolveChosen(_history->peer).get() + : nullptr; +} + void ComposeControls::move(int x, int y) { _wrap->move(x, y); _writeRestricted->move(x, y); @@ -1802,11 +1822,12 @@ void ComposeControls::finishAnimating() { } void ComposeControls::updateControlsGeometry(QSize size) { - // _attachToggle -- _inlineResults ------ _tabbedPanel -- _fieldBarCancel + // _attachToggle (_sendAs) -- _inlineResults ------ _tabbedPanel -- _fieldBarCancel // (_attachDocument|_attachPhoto) _field (_ttlInfo) (_silent|_botCommandStart) _tabbedSelectorToggle _send const auto fieldWidth = size.width() - _attachToggle->width() + - (_sendAs ? _sendAs->width() : 0) - st::historySendRight - _send->width() - _tabbedSelectorToggle->width() @@ -1828,6 +1849,10 @@ void ComposeControls::updateControlsGeometry(QSize size) { auto left = st::historySendRight; _attachToggle->moveToLeft(left, buttonsTop); left += _attachToggle->width(); + if (_sendAs) { + _sendAs->moveToLeft(left, buttonsTop); + left += _sendAs->width(); + } _field->moveToLeft( left, size.height() - _field->height() - st::historySendPadding); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 8cd5282e0..8cc73c8bd 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -49,6 +49,7 @@ namespace Ui { class SendButton; class IconButton; class EmojiButton; +class SendAsButton; class SilentToggle; } // namespace Ui @@ -102,6 +103,7 @@ public: [[nodiscard]] Main::Session &session() const; void setHistory(SetHistoryArgs &&args); void setCurrentDialogsEntryState(Dialogs::EntryState state); + [[nodiscard]] PeerData *sendAsPeer() const; void finishAnimating(); @@ -290,6 +292,7 @@ private: const not_null _tabbedSelectorToggle; const not_null _field; const not_null _botCommandStart; + std::unique_ptr _sendAs; std::unique_ptr _silent; std::unique_ptr _ttlInfo; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 02474893a..e993acd31 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -916,6 +916,7 @@ Api::SendAction RepliesWidget::prepareSendAction( Api::SendOptions options) const { auto result = Api::SendAction(_history, options); result.replyTo = replyToId(); + result.options.sendAs = _composeControls->sendAsPeer(); return result; } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 6230bd041..3aa1bc2c9 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -519,6 +519,7 @@ bool ScheduledWidget::showSendingFilesError( Api::SendAction ScheduledWidget::prepareSendAction( Api::SendOptions options) const { auto result = Api::SendAction(_history, options); + result.options.sendAs = _composeControls->sendAsPeer(); return result; } diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index 41ebdd2f3..35f75c8e6 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -24,6 +24,11 @@ SendAsPeers::SendAsPeers(not_null session) , _onlyMe({ session->user() }) { } +bool SendAsPeers::shouldChoose(not_null peer) { + refresh(peer); + return peer->canWrite() && (list(peer).size() > 1); +} + void SendAsPeers::refresh(not_null peer) { if (!peer->isMegagroup()) { return; diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h index 46d2ad764..35a1db64e 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.h +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -17,6 +17,7 @@ class SendAsPeers final { public: explicit SendAsPeers(not_null session); + bool shouldChoose(not_null peer); void refresh(not_null peer); [[nodiscard]] const std::vector> &list( not_null peer) const; diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp index 62b7af1f3..4b88a7a3f 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -148,14 +148,17 @@ void ChooseSendAsBox( void SetupSendAsButton( not_null button, + rpl::producer active, not_null window) { using namespace rpl::mappers; + const auto current = button->lifetime().make_state< + rpl::variable + >(std::move(active)); button->setClickedCallback([=] { - const auto history = window->activeChatCurrent().history(); - if (!history) { + const auto peer = current->current(); + if (!peer) { return; } - const auto peer = history->peer; const auto session = &peer->session(); const auto &list = session->sendAsPeers().list(peer); if (list.size() < 2) { @@ -171,16 +174,10 @@ void SetupSendAsButton( done)); }); - auto userpic = window->activeChatValue( - ) | rpl::map([=](const Dialogs::Key &key) -> PeerData* { - if (const auto history = key.history()) { - return history->peer->isMegagroup() - ? history->peer.get() - : nullptr; - } - return nullptr; - }) | rpl::filter_nullptr( - ) | rpl::map([=](not_null peer) { + auto userpic = current->value( + ) | rpl::filter([=](PeerData *peer) { + return peer && peer->isMegagroup(); + }) | rpl::map([=](not_null peer) { const auto channel = peer->asMegagroup(); auto updates = rpl::single( @@ -208,7 +205,16 @@ void SetupSendAsButton( ) | rpl::start_with_next([=](QImage &&userpic) { button->setUserpic(std::move(userpic)); }, button->lifetime()); +} +void SetupSendAsButton( + not_null button, + not_null window) { + auto active = window->activeChatValue( + ) | rpl::map([=](const Dialogs::Key &key) { + return key.history() ? key.history()->peer.get() : nullptr; + }); + SetupSendAsButton(button, std::move(active), window); } } // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.h b/Telegram/SourceFiles/ui/chat/choose_send_as.h index eca82cc5b..1af733e1a 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.h +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.h @@ -26,6 +26,11 @@ void ChooseSendAsBox( not_null chosen, Fn)> done); +void SetupSendAsButton( + not_null button, + rpl::producer active, + not_null window); + void SetupSendAsButton( not_null button, not_null window); From 17ffebb684d89b1a2af07cc28aa5848477738e33 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 21:24:45 +0400 Subject: [PATCH 039/180] Fix local display of sent-as from anonymous admin. --- Telegram/SourceFiles/api/api_sending.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index bb7973423..19f0cf03a 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -41,7 +41,7 @@ void InnerFillMessagePostFlags( not_null peer, MessageFlags &flags) { const auto anonymousPost = peer->amAnonymous(); - if (!anonymousPost) { + if (!anonymousPost || options.sendAs) { flags |= MessageFlag::HasFromId; return; } else if (peer->asMegagroup()) { From 3c040ab5f91c359f2eabbd16606b763b9d474ebb Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 21:31:03 +0400 Subject: [PATCH 040/180] Fix send as button refresh. --- .../history_view_compose_controls.cpp | 41 ++++++++++++++----- .../controls/history_view_compose_controls.h | 2 + 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 7068e2b8c..49a8aa7f3 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -663,6 +663,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) { updateControlsGeometry(_wrap->size()); updateControlsVisibility(); updateFieldPlaceholder(); + updateSendAsButton(); //if (!_history) { // return; //} @@ -678,17 +679,6 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) { _wrap.get(), peer->asChannel()); } - if (!session().sendAsPeers().shouldChoose(peer)) { - _sendAs = nullptr; - } else if (!_sendAs) { - _sendAs = std::make_unique( - _wrap.get(), - st::sendAsButton); - Ui::SetupSendAsButton( - _sendAs.get(), - rpl::single(peer.get()), - _window); - } session().local().readDraftsWithCursors(_history); applyDraft(); } @@ -995,6 +985,7 @@ void ComposeControls::init() { initField(); initTabbedSelector(); initSendButton(); + initSendAsButton(); initWriteRestriction(); initVoiceRecordBar(); initKeyHandler(); @@ -1629,6 +1620,17 @@ void ComposeControls::initSendButton() { SendMenu::DefaultScheduleCallback(_wrap.get(), sendMenuType(), send)); } +void ComposeControls::initSendAsButton() { + session().sendAsPeers().updated( + ) | rpl::filter([=](not_null peer) { + return _history && (peer == _history->peer); + }) | rpl::start_with_next([=] { + updateSendAsButton(); + updateControlsVisibility(); + updateControlsGeometry(_wrap->size()); + }, _wrap->lifetime()); +} + void ComposeControls::inlineBotResolveDone( const MTPcontacts_ResolvedPeer &result) { Expects(result.type() == mtpc_contacts_resolvedPeer); @@ -1936,6 +1938,23 @@ void ComposeControls::updateMessagesTTLShown() { } } +void ComposeControls::updateSendAsButton() { + Expects(_history != nullptr); + + const auto peer = _history->peer; + if (!session().sendAsPeers().shouldChoose(peer)) { + _sendAs = nullptr; + } else if (!_sendAs) { + _sendAs = std::make_unique( + _wrap.get(), + st::sendAsButton); + Ui::SetupSendAsButton( + _sendAs.get(), + rpl::single(peer.get()), + _window); + } +} + void ComposeControls::paintBackground(QRect clip) { Painter p(_wrap.get()); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 8cc73c8bd..09b498b48 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -197,6 +197,7 @@ private: void initField(); void initTabbedSelector(); void initSendButton(); + void initSendAsButton(); void initWebpageProcess(); void initWriteRestriction(); void initVoiceRecordBar(); @@ -205,6 +206,7 @@ private: void updateSubmitSettings(); void updateSendButtonType(); void updateMessagesTTLShown(); + void updateSendAsButton(); void updateHeight(); void updateWrappingVisibility(); void updateControlsVisibility(); From dee8b1fe6fc6381bf82bc4e84067dd04b2b5126a Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 21:41:13 +0400 Subject: [PATCH 041/180] Don't attach outgoing items from a channel to each other. --- .../SourceFiles/history/view/history_view_element.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 7cfa00632..2e2c2412a 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -535,11 +535,13 @@ void Element::refreshDataId() { } bool Element::computeIsAttachToPrevious(not_null previous) { - const auto mayBeAttached = [](not_null item) { + const auto mayBeAttached = [](not_null view) { + const auto item = view->data(); return !item->isService() && !item->isEmpty() && !item->isPost() - && (item->from() != item->history()->peer + && (!item->history()->peer->isMegagroup() + || !view->hasOutLayout() || !item->from()->isChannel()); }; const auto item = data(); @@ -547,8 +549,8 @@ bool Element::computeIsAttachToPrevious(not_null previous) { const auto prev = previous->data(); const auto possible = (std::abs(prev->date() - item->date()) < kAttachMessageToPreviousSecondsDelta) - && mayBeAttached(item) - && mayBeAttached(prev); + && mayBeAttached(this) + && mayBeAttached(previous); if (possible) { const auto forwarded = item->Get(); const auto prevForwarded = prev->Get(); From b47e29b182a8b0ed038fb456bc6b1e5b92e051f5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 10 Nov 2021 10:26:37 +0400 Subject: [PATCH 042/180] Fix custom chat themes in scheduled messages. --- Telegram/SourceFiles/history/history_widget.cpp | 2 -- Telegram/SourceFiles/mainwidget.cpp | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 2cbbf128c..2f9de3913 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2203,8 +2203,6 @@ void HistoryWidget::showHistory( refreshTopBarActiveChat(); updateTopBarSelection(); checkMessagesTTL(); - // Restore default theme. - controller()->setChatStyleTheme(controller()->defaultChatTheme()); clearFieldText(); doneShow(); } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 0becb6283..feeece08c 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1391,6 +1391,7 @@ void MainWidget::ui_showPeerHistory( if (noPeer) { _controller->setActiveChatEntry(Dialogs::Key()); + _controller->setChatStyleTheme(controller()->defaultChatTheme()); } if (onlyDialogs) { From dd7568ed3a1f9466028cc620fab6ef86ecddf96f Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 10 Nov 2021 10:38:11 +0400 Subject: [PATCH 043/180] Fix Send As button visibility. --- .../history/view/controls/history_view_compose_controls.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 49a8aa7f3..f4671c7e4 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1892,6 +1892,9 @@ void ComposeControls::updateControlsVisibility() { if (_ttlInfo) { _ttlInfo->show(); } + if (_sendAs) { + _sendAs->show(); + } } bool ComposeControls::updateBotCommandShown() { From fe025e3c44dc2e918f3ef4f6350dd2ce3400e0aa Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 10 Nov 2021 11:00:15 +0400 Subject: [PATCH 044/180] Move Send As phrases to the langpack. --- Telegram/Resources/langs/lang.strings | 2 ++ Telegram/SourceFiles/ui/chat/choose_send_as.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0569a664c..bdf04cba7 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1546,6 +1546,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_broadcast_ph" = "Broadcast a message..."; "lng_broadcast_silent_ph" = "Silent broadcast..."; "lng_send_anonymous_ph" = "Send anonymously..."; +"lng_send_as_title" = "Send message as..."; +"lng_send_as_anonymous_admin" = "Anonymous admin"; "lng_record_cancel" = "Release outside this field to cancel"; "lng_record_lock_cancel_sure" = "Are you sure you want to stop recording and discard your voice message?"; "lng_record_listen_cancel_sure" = "Are you sure you want to discard your recorded voice message?"; diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp index 4b88a7a3f..4daea9956 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -64,7 +64,7 @@ std::unique_ptr ListController::createRow( result->setCustomStatus( tr::lng_group_call_join_as_personal(tr::now)); } else if (peer->isMegagroup()) { - result->setCustomStatus(u"Anonymous admin"_q); + result->setCustomStatus(tr::lng_send_as_anonymous_admin(tr::now)); } else if (const auto channel = peer->asChannel()) { result->setCustomStatus(tr::lng_chat_status_subscribers( tr::now, @@ -111,7 +111,7 @@ void ChooseSendAsBox( Expects(done != nullptr); box->setWidth(st::groupCallJoinAsWidth); - box->setTitle(rpl::single(u"Send message as..."_q)); + box->setTitle(tr::lng_send_as_title()); const auto &labelSt = st::confirmPhoneAboutLabel; box->addRow(object_ptr( box, From f13e28a9c5e411b817ce2b1346db8e600f547adc Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 10 Nov 2021 11:53:01 +0400 Subject: [PATCH 045/180] Use in-class initializers for bitfields. --- Telegram/SourceFiles/boxes/peer_list_box.cpp | 14 ++------------ Telegram/SourceFiles/boxes/peer_list_box.h | 10 +++++----- .../calls/group/calls_group_members_row.cpp | 7 +------ .../calls/group/calls_group_members_row.h | 10 +++++----- Telegram/SourceFiles/data/data_group_call.h | 16 ++++++++-------- 5 files changed, 21 insertions(+), 36 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 9ec96f936..bdf951ee4 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -433,21 +433,11 @@ PeerListRow::PeerListRow(not_null peer) PeerListRow::PeerListRow(not_null peer, PeerListRowId id) : _id(id) -, _peer(peer) -, _hidden(false) -, _initialized(false) -, _isSearchResult(false) -, _isSavedMessagesChat(false) -, _isRepliesMessagesChat(false) { +, _peer(peer) { } PeerListRow::PeerListRow(PeerListRowId id) -: _id(id) -, _hidden(false) -, _initialized(false) -, _isSearchResult(false) -, _isSavedMessagesChat(false) -, _isRepliesMessagesChat(false) { +: _id(id) { } PeerListRow::~PeerListRow() = default; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 950feb1ca..9591dd3ae 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -263,11 +263,11 @@ private: base::flat_set _nameFirstLetters; int _absoluteIndex = -1; State _disabledState = State::Active; - bool _hidden : 1; - bool _initialized : 1; - bool _isSearchResult : 1; - bool _isSavedMessagesChat : 1; - bool _isRepliesMessagesChat : 1; + bool _hidden : 1 = false; + bool _initialized : 1 = false; + bool _isSearchResult : 1 = false; + bool _isSavedMessagesChat : 1 = false; + bool _isRepliesMessagesChat : 1 = false; }; diff --git a/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp b/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp index ec673d57f..aaa7a9cea 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp @@ -126,12 +126,7 @@ MembersRow::MembersRow( not_null delegate, not_null participantPeer) : PeerListRow(participantPeer) -, _delegate(delegate) -, _sounding(false) -, _speaking(false) -, _raisedHandStatus(false) -, _skipLevelUpdate(false) -, _mutedByMe(false) { +, _delegate(delegate) { refreshStatus(); _aboutText = participantPeer->about(); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_members_row.h b/Telegram/SourceFiles/calls/group/calls_group_members_row.h index bbabbbe8c..85f57664d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members_row.h +++ b/Telegram/SourceFiles/calls/group/calls_group_members_row.h @@ -210,11 +210,11 @@ private: crl::time _speakingLastTime = 0; uint64 _raisedHandRating = 0; int _volume = Group::kDefaultVolume; - bool _sounding : 1; - bool _speaking : 1; - bool _raisedHandStatus : 1; - bool _skipLevelUpdate : 1; - bool _mutedByMe : 1; + bool _sounding : 1 = false; + bool _speaking : 1 = false; + bool _raisedHandStatus : 1 = false; + bool _skipLevelUpdate : 1 = false; + bool _mutedByMe : 1 = false; }; diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 9979d7b1d..b1394f7a0 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -32,14 +32,14 @@ struct GroupCallParticipant { uint64 raisedHandRating = 0; uint32 ssrc = 0; int volume = 0; - bool sounding : 1; - bool speaking : 1; - bool additionalSounding : 1; - bool additionalSpeaking : 1; - bool muted : 1; - bool mutedByMe : 1; - bool canSelfUnmute : 1; - bool onlyMinLoaded : 1; + bool sounding : 1 = false; + bool speaking : 1 = false; + bool additionalSounding : 1 = false; + bool additionalSpeaking : 1 = false; + bool muted : 1 = false; + bool mutedByMe : 1 = false; + bool canSelfUnmute : 1 = false; + bool onlyMinLoaded : 1 = false; bool videoJoined = false; bool applyVolumeFromMin = true; From be7cd51740d22f2faa6fd2955978977883e54287 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Nov 2021 15:44:12 +0400 Subject: [PATCH 046/180] Start scrollable CalendarBox. --- Telegram/SourceFiles/boxes/boxes.style | 13 +- .../boxes/peers/edit_participant_box.cpp | 24 +- Telegram/SourceFiles/export/view/export.style | 4 +- .../export/view/export_view_settings.cpp | 25 +- .../SourceFiles/ui/boxes/calendar_box.cpp | 341 ++++++++++-------- Telegram/SourceFiles/ui/boxes/calendar_box.h | 49 +-- .../SourceFiles/ui/boxes/choose_date_time.cpp | 24 +- .../ui/boxes/single_choice_box.cpp | 2 +- .../SourceFiles/ui/boxes/single_choice_box.h | 2 +- Telegram/SourceFiles/window/main_window.h | 11 +- .../window/window_session_controller.cpp | 22 +- 11 files changed, 288 insertions(+), 229 deletions(-) diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index ab0e5c94a..57671b9d9 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -516,14 +516,21 @@ calendarNextDisabled: icon {{ "title_back-flip_horizontal", menuIconFg }}; calendarTitleFont: boxTitleFont; defaultCalendarSizes: CalendarSizes { width: boxWideWidth; - daysHeight: 32px; + daysHeight: 40px; cellSize: size(48px, 40px); cellInner: 34px; - padding: margins(14px, 15px, 14px, 10px); + padding: margins(14px, 7px, 14px, 10px); } calendarDaysFont: normalFont; calendarDaysFg: boxTitleAdditionalFg; -calendarScroll: backgroundScroll; +calendarScroll: ScrollArea(defaultSolidScroll) { + deltat: 3px; + deltab: 3px; + round: 1px; + width: 8px; + deltax: 3px; + hiding: 1000; +} passcodeTextStyle: TextStyle(defaultTextStyle) { lineHeight: 20px; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index ee5365667..e497a625c 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -704,20 +704,22 @@ void EditRestrictedBox::showRestrictUntil() { : base::unixtime::parse(getRealUntilValue()).date(); auto month = highlighted; _restrictUntilBox = Ui::show( - Box( - month, - highlighted, - [this](const QDate &date) { + Box(Ui::CalendarBoxArgs{ + .month = month, + .highlighted = highlighted, + .callback = [=](const QDate &date) { setRestrictUntil( static_cast(date.startOfDay().toSecsSinceEpoch())); - }), + }, + .finalize = [=](not_null box) { + box->addLeftButton( + tr::lng_rights_chat_banned_forever(), + [=] { setRestrictUntil(0); }); + }, + .minDate = tomorrow, + .maxDate = QDate::currentDate().addDays(kMaxRestrictDelayDays), + }), Ui::LayerOption::KeepOther); - _restrictUntilBox->setMaxDate( - QDate::currentDate().addDays(kMaxRestrictDelayDays)); - _restrictUntilBox->setMinDate(tomorrow); - _restrictUntilBox->addLeftButton( - tr::lng_rights_chat_banned_forever(), - [=] { setRestrictUntil(0); }); } void EditRestrictedBox::setRestrictUntil(TimeId until) { diff --git a/Telegram/SourceFiles/export/view/export.style b/Telegram/SourceFiles/export/view/export.style index 4f74e8b54..f45c48e98 100644 --- a/Telegram/SourceFiles/export/view/export.style +++ b/Telegram/SourceFiles/export/view/export.style @@ -97,8 +97,8 @@ exportTopBarLabel: FlatLabel(defaultFlatLabel) { } exportCalendarSizes: CalendarSizes(defaultCalendarSizes) { width: 320px; - daysHeight: 32px; + daysHeight: 40px; cellSize: size(42px, 38px); cellInner: 32px; - padding: margins(14px, 15px, 14px, 10px); + padding: margins(14px, 7px, 14px, 10px); } diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index bf8ecbef3..9745e386f 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -464,12 +464,6 @@ void SettingsWidget::editDateLimit( const auto month = highlighted; const auto shared = std::make_shared>(); const auto finalize = [=](not_null box) { - box->setMaxDate(max - ? base::unixtime::parse(max).date() - : QDate::currentDate()); - box->setMinDate(min - ? base::unixtime::parse(min).date() - : QDate(2013, 8, 1)); // Telegram was launched in August 2013 :) box->addLeftButton(std::move(resetLabel), crl::guard(this, [=] { done(0); if (const auto weak = shared->data()) { @@ -483,12 +477,19 @@ void SettingsWidget::editDateLimit( weak->closeBox(); } }); - auto box = Box( - month, - highlighted, - callback, - finalize, - st::exportCalendarSizes); + auto box = Box(Ui::CalendarBoxArgs{ + .month = month, + .highlighted = highlighted, + .callback = callback, + .finalize = finalize, + .st = st::exportCalendarSizes, + .minDate = (min + ? base::unixtime::parse(min).date() + : QDate(2013, 8, 1)), // Telegram was launched in August 2013 :) + .maxDate = (max + ? base::unixtime::parse(max).date() + : QDate::currentDate()), + }); *shared = Ui::MakeWeak(box.data()); _showBoxCallback(std::move(box)); } diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index bf30631fa..cc5c0be5e 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -8,16 +8,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/calendar_box.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/scroll_area.h" #include "ui/effects/ripple_animation.h" #include "ui/ui_utility.h" #include "lang/lang_keys.h" #include "styles/style_boxes.h" -#include "styles/style_dialogs.h" namespace Ui { namespace { constexpr auto kDaysInWeek = 7; +constexpr auto kMaxDaysForScroll = kDaysInWeek * 1000; } // namespace @@ -25,49 +26,48 @@ class CalendarBox::Context { public: Context(QDate month, QDate highlighted); - void start() { - _month.setForced(_month.value(), true); - } - void setBeginningButton(bool enabled); - bool hasBeginningButton() const { + [[nodiscard]] bool hasBeginningButton() const { return _beginningButton; } void setMinDate(QDate date); void setMaxDate(QDate date); - int minDayIndex() const { + [[nodiscard]] int minDayIndex() const { return _minDayIndex; } - int maxDayIndex() const { + [[nodiscard]] int maxDayIndex() const { return _maxDayIndex; } void skipMonth(int skip); void showMonth(QDate month); - int highlightedIndex() const { + [[nodiscard]] int highlightedIndex() const { return _highlightedIndex; } - int rowsCount() const { + [[nodiscard]] int rowsCount() const { return _rowsCount; } - int daysShift() const { + [[nodiscard]] int rowsCountMax() const { + return 6; + } + [[nodiscard]] int daysShift() const { return _daysShift; } - int daysCount() const { + [[nodiscard]] int daysCount() const { return _daysCount; } - bool isEnabled(int index) const { + [[nodiscard]] bool isEnabled(int index) const { return (index >= _minDayIndex) && (index <= _maxDayIndex); } - bool atBeginning() const { + [[nodiscard]] bool atBeginning() const { return _highlighted == _min; } - const base::Variable &month() { - return _month; + [[nodiscard]] rpl::producer monthValue() const { + return _month.value(); } QDate dateFromIndex(int index) const; @@ -76,14 +76,16 @@ public: private: void applyMonth(const QDate &month, bool forced = false); - static int daysShiftForMonth(QDate month); - static int rowsCountForMonth(QDate month); + static int DaysShiftForMonth(QDate month, QDate min); + static int RowsCountForMonth(QDate month, QDate min, QDate max); bool _beginningButton = false; - base::Variable _month; + rpl::variable _month; QDate _min, _max; QDate _highlighted; + Fn _dayOfWeek; + Fn _monthOfYear; int _highlightedIndex = 0; int _minDayIndex = 0; @@ -94,7 +96,8 @@ private: }; -CalendarBox::Context::Context(QDate month, QDate highlighted) : _highlighted(highlighted) { +CalendarBox::Context::Context(QDate month, QDate highlighted) +: _highlighted(highlighted) { showMonth(month); } @@ -104,12 +107,12 @@ void CalendarBox::Context::setBeginningButton(bool enabled) { void CalendarBox::Context::setMinDate(QDate date) { _min = date; - applyMonth(_month.value(), true); + applyMonth(_month.current(), true); } void CalendarBox::Context::setMaxDate(QDate date) { _max = date; - applyMonth(_month.value(), true); + applyMonth(_month.current(), true); } void CalendarBox::Context::showMonth(QDate month) { @@ -121,21 +124,21 @@ void CalendarBox::Context::showMonth(QDate month) { void CalendarBox::Context::applyMonth(const QDate &month, bool forced) { _daysCount = month.daysInMonth(); - _daysShift = daysShiftForMonth(month); - _rowsCount = rowsCountForMonth(month); + _daysShift = DaysShiftForMonth(month, _min); + _rowsCount = RowsCountForMonth(month, _min, _max); _highlightedIndex = month.daysTo(_highlighted); _minDayIndex = _min.isNull() ? INT_MIN : month.daysTo(_min); _maxDayIndex = _max.isNull() ? INT_MAX : month.daysTo(_max); if (forced) { - _month.setForced(month, true); + _month.force_assign(month); } else { - _month.set(month, true); + _month = month; } } void CalendarBox::Context::skipMonth(int skip) { - auto year = _month.value().year(); - auto month = _month.value().month(); + auto year = _month.current().year(); + auto month = _month.current().month(); month += skip; while (month < 1) { --year; @@ -148,28 +151,58 @@ void CalendarBox::Context::skipMonth(int skip) { showMonth(QDate(year, month, 1)); } -int CalendarBox::Context::daysShiftForMonth(QDate month) { - Assert(!month.isNull()); +int CalendarBox::Context::DaysShiftForMonth(QDate month, QDate min) { + Expects(!month.isNull()); + constexpr auto kMaxRows = 6; - auto inMonthIndex = month.day() - 1; - auto inWeekIndex = month.dayOfWeek() - 1; - return ((kMaxRows * kDaysInWeek) + inWeekIndex - inMonthIndex) % kDaysInWeek; + const auto inMonthIndex = month.day() - 1; + const auto inWeekIndex = month.dayOfWeek() - 1; + const auto from = ((kMaxRows * kDaysInWeek) + inWeekIndex - inMonthIndex) + % kDaysInWeek; + if (min.isNull()) { + min = month.addYears(-1); + } else if (min >= month) { + return from; + } + if (min.day() != 1) { + min = QDate(min.year(), min.month(), 1); + } + const auto add = min.daysTo(month) + inWeekIndex + (min.dayOfWeek() - 1); + return from + add; } -int CalendarBox::Context::rowsCountForMonth(QDate month) { - Assert(!month.isNull()); - auto daysShift = daysShiftForMonth(month); - auto daysCount = month.daysInMonth(); - auto cellsCount = daysShift + daysCount; +int CalendarBox::Context::RowsCountForMonth( + QDate month, + QDate min, + QDate max) { + Expects(!month.isNull()); + + const auto daysShift = DaysShiftForMonth(month, min); + const auto daysCount = month.daysInMonth(); + const auto cellsCount = daysShift + daysCount; auto result = (cellsCount / kDaysInWeek); - if (cellsCount % kDaysInWeek) ++result; - return result; + if (cellsCount % kDaysInWeek) { + ++result; + } + if (max.isNull()) { + max = month.addYears(1); + } + if (max < month.addMonths(1)) { + return result; + } + if (max.day() != 1) { + max = QDate(max.year(), max.month(), 1); + } + max = max.addMonths(1); + max = max.addDays(1 - max.dayOfWeek()); + const auto cellsFull = daysShift + (month.day() - 1) + month.daysTo(max); + return cellsFull / kDaysInWeek; } QDate CalendarBox::Context::dateFromIndex(int index) const { constexpr auto kMonthsCount = 12; - auto month = _month.value().month(); - auto year = _month.value().year(); + auto month = _month.current().month(); + auto year = _month.current().year(); while (index < 0) { if (!--month) { month += kMonthsCount; @@ -197,14 +230,14 @@ QString CalendarBox::Context::labelFromIndex(int index) const { return QString::number(day()); } -class CalendarBox::Inner : public TWidget, private base::Subscriber { +class CalendarBox::Inner final : public RpWidget { public: Inner( QWidget *parent, not_null context, const style::CalendarSizes &st); - int countHeight(); + [[nodiscard]] int countMaxHeight() const; void setDateChosenCallback(Fn callback); void selectBeginning(); @@ -224,19 +257,23 @@ private: int rowsLeft() const; int rowsTop() const; void resizeToCurrent(); - void paintDayNames(Painter &p, QRect clip); void paintRows(Painter &p, QRect clip); const style::CalendarSizes &_st; - not_null _context; + const not_null _context; std::map> _ripples; Fn _dateChosenCallback; - static constexpr auto kEmptySelection = -kDaysInWeek; + static constexpr auto kEmptySelection = INT_MIN / 2; int _selected = kEmptySelection; int _pressed = kEmptySelection; + bool _pointerCursor = false; + bool _cursorSetWithoutMouseMove = false; + + QPoint _lastGlobalPosition; + bool _mouseMoved = false; }; @@ -244,13 +281,15 @@ CalendarBox::Inner::Inner( QWidget *parent, not_null context, const style::CalendarSizes &st) -: TWidget(parent) +: RpWidget(parent) , _st(st) , _context(context) { setMouseTracking(true); - subscribe(context->month(), [this](QDate month) { + + context->monthValue( + ) | rpl::start_with_next([=](QDate month) { monthChanged(month); - }); + }, lifetime()); } void CalendarBox::Inner::monthChanged(QDate month) { @@ -262,7 +301,8 @@ void CalendarBox::Inner::monthChanged(QDate month) { } void CalendarBox::Inner::resizeToCurrent() { - resize(_st.width, countHeight()); + const auto height = _context->rowsCount() * _st.cellSize.height(); + resize(_st.width, _st.padding.top() + height + _st.padding.bottom()); } void CalendarBox::Inner::paintEvent(QPaintEvent *e) { @@ -270,33 +310,15 @@ void CalendarBox::Inner::paintEvent(QPaintEvent *e) { auto clip = e->rect(); - paintDayNames(p, clip); paintRows(p, clip); } -void CalendarBox::Inner::paintDayNames(Painter &p, QRect clip) { - p.setFont(st::calendarDaysFont); - p.setPen(st::calendarDaysFg); - auto y = _st.padding.top(); - auto x = rowsLeft(); - if (!myrtlrect(x, y, _st.cellSize.width() * kDaysInWeek, _st.daysHeight).intersects(clip)) { - return; - } - for (auto i = 0; i != kDaysInWeek; ++i, x += _st.cellSize.width()) { - auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.daysHeight); - if (!rect.intersects(clip)) { - continue; - } - p.drawText(rect, langDayOfWeek(i + 1), style::al_top); - } -} - int CalendarBox::Inner::rowsLeft() const { return _st.padding.left(); } int CalendarBox::Inner::rowsTop() const { - return _st.padding.top() + _st.daysHeight; + return _st.padding.top(); } void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { @@ -304,14 +326,17 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { auto y = rowsTop(); auto index = -_context->daysShift(); auto highlightedIndex = _context->highlightedIndex(); - for (auto row = 0, rowsCount = _context->rowsCount(), daysCount = _context->daysCount() - ; row != rowsCount - ; ++row, y += _st.cellSize.height()) { + const auto daysCount = _context->daysCount(); + const auto rowsCount = _context->rowsCount(); + const auto rowHeight = _st.cellSize.height(); + const auto fromRow = std::max(clip.y() - y, 0) / rowHeight; + const auto tillRow = std::min( + (clip.y() + clip.height() + rowHeight - 1) / rowHeight, + rowsCount); + y += fromRow * rowHeight; + index += fromRow * kDaysInWeek; + for (auto row = fromRow; row != tillRow; ++row, y += rowHeight) { auto x = rowsLeft(); - if (!myrtlrect(x, y, _st.cellSize.width() * kDaysInWeek, _st.cellSize.height()).intersects(clip)) { - index += kDaysInWeek; - continue; - } for (auto col = 0; col != kDaysInWeek; ++col, ++index, x += _st.cellSize.width()) { auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.cellSize.height()); auto grayedOut = (index < 0 || index >= daysCount || !rect.intersects(clip)); @@ -352,6 +377,10 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { } void CalendarBox::Inner::mouseMoveEvent(QMouseEvent *e) { + const auto globalPosition = e->globalPos(); + _mouseMoved = (_lastGlobalPosition != globalPosition); + _lastGlobalPosition = globalPosition; + const auto size = _st.cellSize; const auto point = e->pos(); const auto inner = QRect( @@ -374,9 +403,20 @@ void CalendarBox::Inner::setSelected(int selected) { selected = kEmptySelection; } _selected = selected; - setCursor((_selected == kEmptySelection) - ? style::cur_default - : style::cur_pointer); + const auto pointer = (_selected != kEmptySelection); + const auto force = (_mouseMoved && _cursorSetWithoutMouseMove); + if (_pointerCursor != pointer || force) { + if (force) { + // Workaround some strange bug. When I call setCursor while + // scrolling by touchpad the new cursor is not applied and + // then it is not applied until it is changed. + setCursor(pointer ? style::cur_default : style::cur_pointer); + } + setCursor(pointer ? style::cur_pointer : style::cur_default); + _cursorSetWithoutMouseMove = !_mouseMoved; + _pointerCursor = pointer; + } + _mouseMoved = false; } void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) { @@ -422,9 +462,8 @@ void CalendarBox::Inner::setPressed(int pressed) { } } -int CalendarBox::Inner::countHeight() { - const auto innerHeight = _st.daysHeight - + _context->rowsCount() * _st.cellSize.height(); +int CalendarBox::Inner::countMaxHeight() const { + const auto innerHeight = _context->rowsCountMax() * _st.cellSize.height(); return _st.padding.top() + innerHeight + _st.padding.bottom(); @@ -440,27 +479,41 @@ void CalendarBox::Inner::selectBeginning() { CalendarBox::Inner::~Inner() = default; -class CalendarBox::Title : public TWidget, private base::Subscriber { +class CalendarBox::Title final : public RpWidget { public: - Title(QWidget *parent, not_null context) - : TWidget(parent) - , _context(context) { - subscribe(_context->month(), [this](QDate date) { monthChanged(date); }); - } + Title( + QWidget *parent, + not_null context, + const style::CalendarSizes &st); protected: void paintEvent(QPaintEvent *e); private: void monthChanged(QDate month); + void paintDayNames(Painter &p, QRect clip); - not_null _context; + const style::CalendarSizes &_st; + const not_null _context; QString _text; int _textWidth = 0; }; +CalendarBox::Title::Title( + QWidget *parent, + not_null context, + const style::CalendarSizes &st) +: RpWidget(parent) +, _st(st) +, _context(context) { + _context->monthValue( + ) | rpl::start_with_next([=](QDate date) { + monthChanged(date); + }, lifetime()); +} + void CalendarBox::Title::monthChanged(QDate month) { _text = langMonthOfYearFull(month.month(), month.year()); _textWidth = st::calendarTitleFont->width(_text); @@ -470,77 +523,70 @@ void CalendarBox::Title::monthChanged(QDate month) { void CalendarBox::Title::paintEvent(QPaintEvent *e) { Painter p(this); + const auto clip = e->rect(); + p.setFont(st::calendarTitleFont); p.setPen(st::boxTitleFg); - p.drawTextLeft((width() - _textWidth) / 2, (height() - st::calendarTitleFont->height) / 2, width(), _text, _textWidth); + p.drawTextLeft((width() - _textWidth) / 2, (st::calendarTitleHeight - st::calendarTitleFont->height) / 2, width(), _text, _textWidth); + + paintDayNames(p, clip); } -CalendarBox::CalendarBox( - QWidget*, - QDate month, - QDate highlighted, - Fn callback, - FnMut)> finalize) -: CalendarBox( - nullptr, - month, - highlighted, - std::move(callback), - std::move(finalize), - st::defaultCalendarSizes) { +void CalendarBox::Title::paintDayNames(Painter &p, QRect clip) { + p.setFont(st::calendarDaysFont); + p.setPen(st::calendarDaysFg); + auto y = st::calendarTitleHeight + _st.padding.top(); + auto x = _st.padding.left(); + if (!myrtlrect(x, y, _st.cellSize.width() * kDaysInWeek, _st.daysHeight).intersects(clip)) { + return; + } + for (auto i = 0; i != kDaysInWeek; ++i, x += _st.cellSize.width()) { + auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.daysHeight); + if (!rect.intersects(clip)) { + continue; + } + p.drawText(rect, langDayOfWeek(i + 1), style::al_top); + } } -CalendarBox::CalendarBox( - QWidget*, - QDate month, - QDate highlighted, - Fn callback, - FnMut)> finalize, - const style::CalendarSizes &st) -: _st(st) -, _context(std::make_unique(month, highlighted)) -, _inner(this, _context.get(), _st) -, _title(this, _context.get()) +CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args) +: _st(args.st) +, _context( + std::make_unique(args.month.value(), args.highlighted.value())) +, _scroll(std::make_unique(this, st::calendarScroll)) +, _inner( + _scroll->setOwnedWidget(object_ptr(this, _context.get(), _st))) +, _title(this, _context.get(), _st) , _previous(this, st::calendarPrevious) , _next(this, st::calendarNext) -, _callback(std::move(callback)) -, _finalize(std::move(finalize)) { -} - -void CalendarBox::setMinDate(QDate date) { - _context->setMinDate(date); -} - -void CalendarBox::setMaxDate(QDate date) { - _context->setMaxDate(date); -} - -bool CalendarBox::hasBeginningButton() const { - return _context->hasBeginningButton(); -} - -void CalendarBox::setBeginningButton(bool enabled) { - _context->setBeginningButton(enabled); +, _callback(std::move(args.callback.value())) +, _finalize(std::move(args.finalize)) { + _title->setAttribute(Qt::WA_TransparentForMouseEvents); + _context->setBeginningButton(args.hasBeginningButton); + _context->setMinDate(args.minDate); + _context->setMaxDate(args.maxDate); } void CalendarBox::prepare() { - _previous->setClickedCallback([this] { goPreviousMonth(); }); - _next->setClickedCallback([this] { goNextMonth(); }); + _previous->setClickedCallback([=] { goPreviousMonth(); }); + _next->setClickedCallback([=] { goNextMonth(); }); -// _inner = setInnerWidget(object_ptr(this, _context.get()), st::calendarScroll, st::calendarTitleHeight); _inner->setDateChosenCallback(std::move(_callback)); - addButton(tr::lng_close(), [this] { closeBox(); }); + addButton(tr::lng_close(), [=] { closeBox(); }); - subscribe(_context->month(), [this](QDate month) { monthChanged(month); }); - - _context->start(); + _context->monthValue( + ) | rpl::start_with_next([=](QDate month) { + monthChanged(month); + }, lifetime()); if (_finalize) { _finalize(this); } - if (!_context->atBeginning() && hasBeginningButton()) { - addLeftButton(tr::lng_calendar_beginning(), [this] { _inner->selectBeginning(); }); + if (!_context->atBeginning() && _context->hasBeginningButton()) { + addLeftButton(tr::lng_calendar_beginning(), [=] { + _inner->selectBeginning(); + }); } } @@ -565,7 +611,7 @@ void CalendarBox::goNextMonth() { } void CalendarBox::monthChanged(QDate month) { - setDimensions(_st.width, st::calendarTitleHeight + _inner->countHeight()); + setDimensions(_st.width, st::calendarTitleHeight + _st.daysHeight + _inner->countMaxHeight()); auto previousEnabled = isPreviousEnabled(); _previous->setIconOverride(previousEnabled ? nullptr : &st::calendarPreviousDisabled); _previous->setRippleColorOverride(previousEnabled ? nullptr : &st::boxBg); @@ -579,8 +625,9 @@ void CalendarBox::monthChanged(QDate month) { void CalendarBox::resizeEvent(QResizeEvent *e) { _previous->moveToLeft(0, 0); _next->moveToRight(0, 0); - _title->setGeometryToLeft(_previous->width(), 0, width() - _previous->width() - _next->width(), st::calendarTitleHeight); - _inner->setGeometryToLeft(0, st::calendarTitleHeight, width(), height() - st::calendarTitleHeight); + const auto title = st::calendarTitleHeight + _st.daysHeight; + _title->setGeometryToLeft(0, 0, width(), title); + _scroll->setGeometryToLeft(0, title, width(), height() - title); BoxContent::resizeEvent(e); } @@ -589,9 +636,9 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) { e->ignore(); } else if (e->key() == Qt::Key_Home) { _inner->selectBeginning(); - } else if (e->key() == Qt::Key_Left) { + } else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) { goPreviousMonth(); - } else if (e->key() == Qt::Key_Right) { + } else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down) { goNextMonth(); } } diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h index b5e398b6b..830dcebb5 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -8,38 +8,39 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/layers/box_content.h" -#include "base/observer.h" +#include "base/required.h" namespace style { struct CalendarSizes; } // namespace style +namespace st { +extern const style::CalendarSizes &defaultCalendarSizes; +} // namespace st + namespace Ui { class IconButton; +class ScrollArea; +class CalendarBox; -class CalendarBox : public BoxContent, private base::Subscriber { +struct CalendarBoxArgs { + template + using required = base::required; + + required month; + required highlighted; + required> callback; + FnMut)> finalize; + const style::CalendarSizes &st = st::defaultCalendarSizes; + QDate minDate; + QDate maxDate; + bool hasBeginningButton = false; +}; + +class CalendarBox final : public BoxContent { public: - CalendarBox( - QWidget*, - QDate month, - QDate highlighted, - Fn callback, - FnMut)> finalize = nullptr); - CalendarBox( - QWidget*, - QDate month, - QDate highlighted, - Fn callback, - FnMut)> finalize, - const style::CalendarSizes &st); - - void setBeginningButton(bool enabled); - bool hasBeginningButton() const; - - void setMinDate(QDate date); - void setMaxDate(QDate date); - + CalendarBox(QWidget*, CalendarBoxArgs &&args); ~CalendarBox(); protected: @@ -63,8 +64,10 @@ private: class Context; std::unique_ptr _context; + std::unique_ptr _scroll; + class Inner; - object_ptr _inner; + not_null _inner; class Title; object_ptr _title; diff --git a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp index f1532f8a2..023f7a94c 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_date_time.cpp @@ -159,19 +159,17 @@ ChooseDateTimeBoxDescriptor ChooseDateTimeBox( if (*calendar) { return; } - const auto chosen = [=](QDate chosen) { - state->date = chosen; - (*calendar)->closeBox(); - }; - const auto finalize = [=](not_null<CalendarBox*> box) { - box->setMinDate(minDate()); - box->setMaxDate(maxDate()); - }; - *calendar = box->getDelegate()->show(Box<CalendarBox>( - state->date.current(), - state->date.current(), - crl::guard(box, chosen), - finalize)); + *calendar = box->getDelegate()->show( + Box<CalendarBox>(Ui::CalendarBoxArgs{ + .month = state->date.current(), + .highlighted = state->date.current(), + .callback = crl::guard(box, [=](QDate chosen) { + state->date = chosen; + (*calendar)->closeBox(); + }), + .minDate = minDate(), + .maxDate = maxDate(), + })); (*calendar)->boxClosing( ) | rpl::start_with_next(crl::guard(state->time, [=] { state->time->setFocusFast(); diff --git a/Telegram/SourceFiles/ui/boxes/single_choice_box.cpp b/Telegram/SourceFiles/ui/boxes/single_choice_box.cpp index a68e1cfa4..a75d8a030 100644 --- a/Telegram/SourceFiles/ui/boxes/single_choice_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/single_choice_box.cpp @@ -44,7 +44,7 @@ void SingleChoiceBox( st::boxPadding.right(), st::boxOptionListSkip)); } - const auto callback = args.callback; + const auto callback = args.callback.value(); group->setChangedCallback([=](int value) { const auto weak = Ui::MakeWeak(box); callback(value); diff --git a/Telegram/SourceFiles/ui/boxes/single_choice_box.h b/Telegram/SourceFiles/ui/boxes/single_choice_box.h index b33d13575..4964d3b05 100644 --- a/Telegram/SourceFiles/ui/boxes/single_choice_box.h +++ b/Telegram/SourceFiles/ui/boxes/single_choice_box.h @@ -22,7 +22,7 @@ struct SingleChoiceBoxArgs { required<rpl::producer<QString>> title; const std::vector<QString> &options; int initialSelection = 0; - Fn<void(int)> callback; + required<Fn<void(int)>> callback; const style::Checkbox *st = nullptr; const style::Radio *radioSt = nullptr; }; diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index fe6d9fc9f..99c30d392 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -42,10 +42,13 @@ struct TermsLock; void ConvertIconToBlack(QImage &image); struct CounterLayerArgs { - base::required<int> size = 16; - base::required<int> count = 1; - base::required<style::color> bg; - base::required<style::color> fg; + template <typename T> + using required = base::required<T>; + + required<int> size = 16; + required<int> count = 1; + required<style::color> bg; + required<style::color> fg; }; [[nodiscard]] QImage GenerateCounterLayer(CounterLayerArgs &&args); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 6b28437fb..d4454785d 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1209,18 +1209,16 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { : !currentPeerDate.isNull() ? currentPeerDate : QDate::currentDate(); - const auto month = highlighted; - auto callback = [=](const QDate &date) { - session().api().jumpToDate(chat, date); - }; - auto box = Box<Ui::CalendarBox>( - month, - highlighted, - std::move(callback)); - box->setMinDate(minPeerDate(chat)); - box->setMaxDate(maxPeerDate(chat)); - box->setBeginningButton(true); - show(std::move(box)); + show(Box<Ui::CalendarBox>(Ui::CalendarBoxArgs{ + .month = highlighted, + .highlighted = highlighted, + .callback = [=](const QDate &date) { + session().api().jumpToDate(chat, date); + }, + .minDate = minPeerDate(chat), + .maxDate = maxPeerDate(chat), + .hasBeginningButton = true, + })); } void SessionController::showPassportForm(const Passport::FormRequest &request) { From b9b609f4454a1fc1066546a98feab2a38305b2c1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 11 Nov 2021 17:03:06 +0400 Subject: [PATCH 047/180] Track month in CalendarBox while scrolling. --- Telegram/SourceFiles/boxes/boxes.style | 2 +- Telegram/SourceFiles/export/view/export.style | 2 +- .../SourceFiles/ui/boxes/calendar_box.cpp | 47 ++++++++++++++++++- Telegram/SourceFiles/ui/boxes/calendar_box.h | 3 ++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 57671b9d9..e868a3bea 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -519,7 +519,7 @@ defaultCalendarSizes: CalendarSizes { daysHeight: 40px; cellSize: size(48px, 40px); cellInner: 34px; - padding: margins(14px, 7px, 14px, 10px); + padding: margins(14px, 0px, 14px, 0px); } calendarDaysFont: normalFont; calendarDaysFg: boxTitleAdditionalFg; diff --git a/Telegram/SourceFiles/export/view/export.style b/Telegram/SourceFiles/export/view/export.style index f45c48e98..5091de896 100644 --- a/Telegram/SourceFiles/export/view/export.style +++ b/Telegram/SourceFiles/export/view/export.style @@ -100,5 +100,5 @@ exportCalendarSizes: CalendarSizes(defaultCalendarSizes) { daysHeight: 40px; cellSize: size(42px, 38px); cellInner: 32px; - padding: margins(14px, 7px, 14px, 10px); + padding: margins(14px, 0px, 14px, 0px); } diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index cc5c0be5e..485f62f0e 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -43,6 +43,7 @@ public: void skipMonth(int skip); void showMonth(QDate month); + [[nodiscard]] bool showsMonthOf(QDate date) const; [[nodiscard]] int highlightedIndex() const { return _highlightedIndex; @@ -122,6 +123,11 @@ void CalendarBox::Context::showMonth(QDate month) { applyMonth(month); } +bool CalendarBox::Context::showsMonthOf(QDate date) const { + const auto shown = _month.current(); + return (shown.year() == date.year()) && (shown.month() == date.month()); +} + void CalendarBox::Context::applyMonth(const QDate &month, bool forced) { _daysCount = month.daysInMonth(); _daysShift = DaysShiftForMonth(month, _min); @@ -167,7 +173,7 @@ int CalendarBox::Context::DaysShiftForMonth(QDate month, QDate min) { if (min.day() != 1) { min = QDate(min.year(), min.month(), 1); } - const auto add = min.daysTo(month) + inWeekIndex + (min.dayOfWeek() - 1); + const auto add = min.daysTo(month) - inWeekIndex + (min.dayOfWeek() - 1); return from + add; } @@ -565,6 +571,13 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args) _context->setBeginningButton(args.hasBeginningButton); _context->setMinDate(args.minDate); _context->setMaxDate(args.maxDate); + + _scroll->scrolls( + ) | rpl::filter([=] { + return _watchScroll; + }) | rpl::start_with_next([=] { + processScroll(); + }, lifetime()); } void CalendarBox::prepare() { @@ -579,6 +592,7 @@ void CalendarBox::prepare() { ) | rpl::start_with_next([=](QDate month) { monthChanged(month); }, lifetime()); + setExactScroll(); if (_finalize) { _finalize(this); @@ -601,15 +615,46 @@ bool CalendarBox::isNextEnabled() const { void CalendarBox::goPreviousMonth() { if (isPreviousEnabled()) { _context->skipMonth(-1); + setExactScroll(); } } void CalendarBox::goNextMonth() { if (isNextEnabled()) { _context->skipMonth(1); + setExactScroll(); } } +void CalendarBox::setExactScroll() { + const auto top = _st.padding.top() + + (_context->daysShift() / kDaysInWeek) * _st.cellSize.height(); + _scroll->scrollToY(top); + _watchScroll = true; +} + +void CalendarBox::processScroll() { + const auto wasTop = _scroll->scrollTop(); + const auto wasShift = _context->daysShift(); + const auto point = _scroll->rect().center() + QPoint(0, wasTop); + const auto row = (point.y() - _st.padding.top()) / _st.cellSize.height(); + const auto col = (point.x() - _st.padding.left()) / _st.cellSize.width(); + const auto index = row * kDaysInWeek + col; + const auto date = _context->dateFromIndex(index - wasShift); + if (_context->showsMonthOf(date)) { + return; + } + const auto wasFirst = _context->dateFromIndex(-wasShift); + const auto month = QDate(date.year(), date.month(), 1); + _watchScroll = false; + _context->showMonth(month); + const auto nowShift = _context->daysShift(); + const auto nowFirst = _context->dateFromIndex(-nowShift); + const auto delta = nowFirst.daysTo(wasFirst) / kDaysInWeek; + _scroll->scrollToY(wasTop + delta * _st.cellSize.height()); + _watchScroll = true; +} + void CalendarBox::monthChanged(QDate month) { setDimensions(_st.width, st::calendarTitleHeight + _st.daysHeight + _inner->countMaxHeight()); auto previousEnabled = isPreviousEnabled(); diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h index 830dcebb5..b3952dc87 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -58,6 +58,8 @@ private: void goPreviousMonth(); void goNextMonth(); + void setExactScroll(); + void processScroll(); const style::CalendarSizes &_st; @@ -76,6 +78,7 @@ private: Fn<void(QDate date)> _callback; FnMut<void(not_null<CalendarBox*>)> _finalize; + bool _watchScroll = false; }; From 15dc6064ef9c77b2a63a7e3f288a9e2e10f42541 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 11 Nov 2021 18:24:45 +0400 Subject: [PATCH 048/180] Improve CalendarBox title design for vertical layout. --- Telegram/Resources/icons/calendar_down.png | Bin 0 -> 188 bytes Telegram/Resources/icons/calendar_down@2x.png | Bin 0 -> 291 bytes Telegram/Resources/icons/calendar_down@3x.png | Bin 0 -> 867 bytes Telegram/SourceFiles/boxes/boxes.style | 10 ++--- .../SourceFiles/ui/boxes/calendar_box.cpp | 36 +++++++++++------- Telegram/SourceFiles/ui/boxes/calendar_box.h | 1 - 6 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 Telegram/Resources/icons/calendar_down.png create mode 100644 Telegram/Resources/icons/calendar_down@2x.png create mode 100644 Telegram/Resources/icons/calendar_down@3x.png diff --git a/Telegram/Resources/icons/calendar_down.png b/Telegram/Resources/icons/calendar_down.png new file mode 100644 index 0000000000000000000000000000000000000000..e7213ec3665dfded99b94bb5e3a036b5e98c62ac GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP%zKa z#WBR9_w3|@oCgeen5XhRWZd%KB+8?<#Yyvq;n~HePbRyh7b`M1Yz#c^_d~s*qd{dt zPP>PbkP_2`l-tu%CLNnHOK;M?^bdDdD4e|eWr6U#2GNv@3*KFtu}*I5!{yViPKcI{ j-+N|<M*i#H`wN&(u3}KTk+ttR&}IftS3j3^P6<r_--Sj= literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calendar_down@2x.png b/Telegram/Resources/icons/calendar_down@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8ee33ea618e9bd6132664dfcf7dc3706796770ce GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oVGw3ym^DWND0tP= z#W5tJ_3bo6zQYPUu5<JL--$W7Tbw^f=A^9BEwwFz35!Cwrfp)m{ho8e<kpTJy`JM! zKWG}TE<51XFjKR|fx)ktae)An;pb<24L3g(5M)l+!nY!kpFwz{c>B!jTm64T3RVB` zYucyRH|Ynf_{2HIbDm6G@#gjv8>xR{5}Frp91nfdI-N<b^V}}OvohAt@B2O9`14%J zfunEtUtN{8>Q&=)P8m;&hhLdYh2}c1*1X9O;kB}1`IM^TWyOw)4;#L7rgTayJ}MC_ nzeY7WM)dz@e+$+;?GG$XHvbshY?@1f9%S%z^>bP0l+XkK1rKtb literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calendar_down@3x.png b/Telegram/Resources/icons/calendar_down@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f48c3def0d04fdce670e64999a56b995da532e6f GIT binary patch literal 867 zcmV-p1DyPcP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00009a7bBm000XU z000XU0RWnu7ytkR6-h)vRA_<im_1DDP#A_!3+d3MQ@4(FMVUxM9oYOySp6ATWn<Kp zghf}vqGLB5EIZMKh3Eo_;;2k?f{>ClY5F>la#h-M`04drZ~7*k+VB0Iw{Xt+Qh^Wx z@{1+%23S>b0N#oN@Kzjvx8eZ2^(BC&X{6a~l1wJUizif>OolX@O`>Vq%smi7pzAt% zy&eEyG#Vk7%fY|L`^x2V7>z~%fL^bMwY9a+_X##OHegv6KFe_&<nwv%@cDcmj^lh` zvn&hgbb9j4_VzXgg8_o%x-JTZ0yp?Vp#ax)1K9?H0d{tFK<@7D0w3_@c^*oo(vQQJ zN+o!nM`bfj6GW0E+}zwy9|VAMx%^}B<#L(Hb9;LWNs=aOAdyJm>gtMFRIOGa2*O_j z7X$&-YL&@xeSM8YBH`ElDZaS4U=|yOfn+inIeapigkc!W{+E}R-(TWaqqDO!rip2q zkYzbCcv+TVnkKXF`T2QJ{j6cJSPWhLz8Z}N6h&DXUQrY@8VzbM*L6`W7Q;M(jgOCy zXZGLkYPDKWRds22RaMbywWvKD$HB?T3C$<G!T$a}Y}=+bczJn&rfCbqYnq1V=Vz+7 zZQD3FIAD3v8_^CrU+?el&~<%ocwN`=_Vz~goE}tcKX${QQRBNT%fkBlI@2qaO3mdp z9TlPo;J-D0*rUC*t*xz4k8C!}{51oHwP*tP|J!c2O9ilP8+&_uGqt<ByUg|U`ue)e zdRlDpsj7;{$44rN<2X1vI-1rT9v(7xw5O-1Wp}iN3!o?p?(grZK>pr+dV0z{Qyv~3 zRyk9aDuOJ_sMqUMFwgUt6J@<#U-eX9x)72i%{9M-$uJCmqT~~R5CTyYQLEJ!hOgCX z5JhoS_|*gWSEW*!8@^JhMA9?T0E7^9Ivpl_r_+fNKH31Ao0}Mq$D#1!@fg``_TLRa z$YfL)4u>=F!{HFAR4Qut=mQWk89Vy@{uJJ_tc8X>?g0pyj2-QE8|`*`sd0PWOYl?t tR{dWecYmYe0K63k;H@|SZ^Z$4>jQXfOWEie75M-F002ovPDHLkV1l}0mn;AP literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index e868a3bea..3e519d40f 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -492,8 +492,8 @@ calendarPrevious: IconButton { width: calendarTitleHeight; height: calendarTitleHeight; - icon: icon {{ "title_back", boxTitleFg }}; - iconPosition: point(20px, 20px); + icon: icon {{ "calendar_down-flip_vertical", boxTitleFg }}; + iconPosition: point(-1px, -1px); rippleAreaPosition: point(6px, 6px); rippleAreaSize: 44px; @@ -501,9 +501,9 @@ calendarPrevious: IconButton { color: windowBgOver; } } -calendarPreviousDisabled: icon {{ "title_back", menuIconFg }}; +calendarPreviousDisabled: icon {{ "calendar_down-flip_vertical", menuIconFg }}; calendarNext: IconButton(calendarPrevious) { - icon: icon {{ "title_back-flip_horizontal", boxTitleFg }}; + icon: icon {{ "calendar_down", boxTitleFg }}; } CalendarSizes { width: pixels; @@ -512,7 +512,7 @@ CalendarSizes { cellInner: pixels; padding: margins; } -calendarNextDisabled: icon {{ "title_back-flip_horizontal", menuIconFg }}; +calendarNextDisabled: icon {{ "calendar_down", menuIconFg }}; calendarTitleFont: boxTitleFont; defaultCalendarSizes: CalendarSizes { width: boxWideWidth; diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 485f62f0e..8e96b29c1 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -504,6 +504,7 @@ private: QString _text; int _textWidth = 0; + int _textLeft = 0; }; @@ -514,6 +515,9 @@ CalendarBox::Title::Title( : RpWidget(parent) , _st(st) , _context(context) { + const auto dayWidth = st::calendarDaysFont->width(langDayOfWeek(1)); + _textLeft = _st.padding.left() + (_st.cellSize.width() - dayWidth) / 2; + _context->monthValue( ) | rpl::start_with_next([=](QDate date) { monthChanged(date); @@ -533,7 +537,12 @@ void CalendarBox::Title::paintEvent(QPaintEvent *e) { p.setFont(st::calendarTitleFont); p.setPen(st::boxTitleFg); - p.drawTextLeft((width() - _textWidth) / 2, (st::calendarTitleHeight - st::calendarTitleFont->height) / 2, width(), _text, _textWidth); + p.drawTextLeft( + _textLeft, + (st::calendarTitleHeight - st::calendarTitleFont->height) / 2, + width(), + _text, + _textWidth); paintDayNames(p, clip); } @@ -614,6 +623,7 @@ bool CalendarBox::isNextEnabled() const { void CalendarBox::goPreviousMonth() { if (isPreviousEnabled()) { + _watchScroll = false; _context->skipMonth(-1); setExactScroll(); } @@ -621,6 +631,7 @@ void CalendarBox::goPreviousMonth() { void CalendarBox::goNextMonth() { if (isNextEnabled()) { + _watchScroll = false; _context->skipMonth(1); setExactScroll(); } @@ -668,8 +679,17 @@ void CalendarBox::monthChanged(QDate month) { } void CalendarBox::resizeEvent(QResizeEvent *e) { - _previous->moveToLeft(0, 0); - _next->moveToRight(0, 0); + const auto dayWidth = st::calendarDaysFont->width(langDayOfWeek(7)); + const auto skip = _st.padding.left() + + _st.cellSize.width() * (kDaysInWeek - 1) + + (_st.cellSize.width() - dayWidth) / 2 + + dayWidth; + const auto right = width() - skip; + const auto shift = _next->width() + - (_next->width() - st::calendarPrevious.icon.width()) / 2 + - st::calendarPrevious.icon.width(); + _next->moveToRight(right - shift, 0); + _previous->moveToRight(right - shift + _next->width(), 0); const auto title = st::calendarTitleHeight + _st.daysHeight; _title->setGeometryToLeft(0, 0, width(), title); _scroll->setGeometryToLeft(0, title, width(), height() - title); @@ -688,16 +708,6 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) { } } -void CalendarBox::wheelEvent(QWheelEvent *e) { - const auto direction = Ui::WheelDirection(e); - - if (direction < 0) { - goPreviousMonth(); - } else if (direction > 0) { - goNextMonth(); - } -} - CalendarBox::~CalendarBox() = default; } // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h index b3952dc87..a5115ffce 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -48,7 +48,6 @@ protected: void keyPressEvent(QKeyEvent *e) override; void resizeEvent(QResizeEvent *e) override; - void wheelEvent(QWheelEvent *e) override; private: void monthChanged(QDate month); From 4414369fc86a52be7bf0267e86d38f240f0a5aaf Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 15 Nov 2021 13:09:44 +0400 Subject: [PATCH 049/180] Implement jump-to-start / end by long press in CalendarBox. --- Telegram/Resources/langs/lang.strings | 4 +- .../SourceFiles/ui/boxes/calendar_box.cpp | 130 ++++++++++++++---- Telegram/SourceFiles/ui/boxes/calendar_box.h | 20 ++- .../window/window_session_controller.cpp | 86 ++++++------ 4 files changed, 165 insertions(+), 75 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index bdf04cba7..a08ae7ded 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -78,7 +78,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_month_day_year" = "{month} {day}, {year}"; "lng_month_year" = "{month} {year}"; -"lng_calendar_beginning" = "Beginning"; +"lng_calendar_select_days" = "Select days"; +"lng_calendar_start_tip" = "Press and hold to jump to the start."; +"lng_calendar_end_tip" = "Press and hold to jump to the end."; "lng_box_ok" = "OK"; "lng_box_done" = "Done"; diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 8e96b29c1..5673eb42f 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -19,6 +19,8 @@ namespace { constexpr auto kDaysInWeek = 7; constexpr auto kMaxDaysForScroll = kDaysInWeek * 1000; +constexpr auto kTooltipDelay = crl::time(1000); +constexpr auto kJumpDelay = 2 * crl::time(1000); } // namespace @@ -26,9 +28,9 @@ class CalendarBox::Context { public: Context(QDate month, QDate highlighted); - void setBeginningButton(bool enabled); - [[nodiscard]] bool hasBeginningButton() const { - return _beginningButton; + void setHasSelection(bool has); + [[nodiscard]] bool hasSelection() const { + return _hasSelection; } void setMinDate(QDate date); @@ -63,9 +65,6 @@ public: [[nodiscard]] bool isEnabled(int index) const { return (index >= _minDayIndex) && (index <= _maxDayIndex); } - [[nodiscard]] bool atBeginning() const { - return _highlighted == _min; - } [[nodiscard]] rpl::producer<QDate> monthValue() const { return _month.value(); @@ -80,7 +79,7 @@ private: static int DaysShiftForMonth(QDate month, QDate min); static int RowsCountForMonth(QDate month, QDate min, QDate max); - bool _beginningButton = false; + bool _hasSelection = false; rpl::variable<QDate> _month; QDate _min, _max; @@ -102,8 +101,8 @@ CalendarBox::Context::Context(QDate month, QDate highlighted) showMonth(month); } -void CalendarBox::Context::setBeginningButton(bool enabled) { - _beginningButton = enabled; +void CalendarBox::Context::setHasSelection(bool has) { + _hasSelection = has; } void CalendarBox::Context::setMinDate(QDate date) { @@ -245,7 +244,6 @@ public: [[nodiscard]] int countMaxHeight() const; void setDateChosenCallback(Fn<void(QDate)> callback); - void selectBeginning(); ~Inner(); @@ -479,10 +477,6 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) { _dateChosenCallback = std::move(callback); } -void CalendarBox::Inner::selectBeginning() { - _dateChosenCallback(_context->dateFromIndex(_context->minDayIndex())); -} - CalendarBox::Inner::~Inner() = default; class CalendarBox::Title final : public RpWidget { @@ -575,9 +569,10 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args) , _previous(this, st::calendarPrevious) , _next(this, st::calendarNext) , _callback(std::move(args.callback.value())) -, _finalize(std::move(args.finalize)) { +, _finalize(std::move(args.finalize)) +, _jumpTimer([=] { jump(_jumpButton); }) { _title->setAttribute(Qt::WA_TransparentForMouseEvents); - _context->setBeginningButton(args.hasBeginningButton); + _context->setHasSelection(args.allowsSelection); _context->setMinDate(args.minDate); _context->setMaxDate(args.maxDate); @@ -587,6 +582,60 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args) }) | rpl::start_with_next([=] { processScroll(); }, lifetime()); + + const auto setupJumps = [&]( + not_null<IconButton*> button, + not_null<bool*> enabled) { + button->events( + ) | rpl::filter([=] { + return *enabled; + }) | rpl::start_with_next([=](not_null<QEvent*> e) { + const auto type = e->type(); + if (type == QEvent::MouseMove + && !(static_cast<QMouseEvent*>(e.get())->buttons() + & Qt::LeftButton)) { + showJumpTooltip(button); + } else if (type == QEvent::Leave) { + Ui::Tooltip::Hide(); + } else if (type == QEvent::MouseButtonPress + && (static_cast<QMouseEvent*>(e.get())->button() + == Qt::LeftButton)) { + jumpAfterDelay(button); + } else if (type == QEvent::MouseButtonRelease + && (static_cast<QMouseEvent*>(e.get())->button() + == Qt::LeftButton)) { + _jumpTimer.cancel(); + } + }, lifetime()); + }; + setupJumps(_previous.data(), &_previousEnabled); + setupJumps(_next.data(), &_nextEnabled); +} + +void CalendarBox::showJumpTooltip(not_null<IconButton*> button) { + _tooltipButton = button; + Ui::Tooltip::Show(kTooltipDelay, this); +} + +void CalendarBox::jumpAfterDelay(not_null<IconButton*> button) { + _jumpButton = button; + _jumpTimer.callOnce(kJumpDelay); + Ui::Tooltip::Hide(); +} + +void CalendarBox::jump(QPointer<IconButton> button) { + const auto jumpToIndex = [&](int index) { + _watchScroll = false; + _context->showMonth(_context->dateFromIndex(index)); + setExactScroll(); + }; + if (_jumpButton == _previous.data() && _previousEnabled) { + jumpToIndex(_context->minDayIndex()); + } else if (_jumpButton == _next.data() && _nextEnabled) { + jumpToIndex(_context->maxDayIndex()); + } + _jumpButton = nullptr; + _jumpTimer.cancel(); } void CalendarBox::prepare() { @@ -606,9 +655,8 @@ void CalendarBox::prepare() { if (_finalize) { _finalize(this); } - if (!_context->atBeginning() && _context->hasBeginningButton()) { - addLeftButton(tr::lng_calendar_beginning(), [=] { - _inner->selectBeginning(); + if (_context->hasSelection()) { + addLeftButton(tr::lng_calendar_select_days(), [=] { }); } } @@ -666,16 +714,40 @@ void CalendarBox::processScroll() { _watchScroll = true; } +QString CalendarBox::tooltipText() const { + if (_tooltipButton == _previous.data()) { + return tr::lng_calendar_start_tip(tr::now); + } else if (_tooltipButton == _next.data()) { + return tr::lng_calendar_end_tip(tr::now); + } + return QString(); +} + +QPoint CalendarBox::tooltipPos() const { + return QCursor::pos(); +} + +bool CalendarBox::tooltipWindowActive() const { + return window()->isActiveWindow(); +} + void CalendarBox::monthChanged(QDate month) { setDimensions(_st.width, st::calendarTitleHeight + _st.daysHeight + _inner->countMaxHeight()); - auto previousEnabled = isPreviousEnabled(); - _previous->setIconOverride(previousEnabled ? nullptr : &st::calendarPreviousDisabled); - _previous->setRippleColorOverride(previousEnabled ? nullptr : &st::boxBg); - _previous->setCursor(previousEnabled ? style::cur_pointer : style::cur_default); - auto nextEnabled = isNextEnabled(); - _next->setIconOverride(nextEnabled ? nullptr : &st::calendarNextDisabled); - _next->setRippleColorOverride(nextEnabled ? nullptr : &st::boxBg); - _next->setCursor(nextEnabled ? style::cur_pointer : style::cur_default); + + _previousEnabled = isPreviousEnabled(); + _previous->setIconOverride(_previousEnabled ? nullptr : &st::calendarPreviousDisabled); + _previous->setRippleColorOverride(_previousEnabled ? nullptr : &st::boxBg); + _previous->setCursor(_previousEnabled ? style::cur_pointer : style::cur_default); + if (!_previousEnabled) { + _previous->clearState(); + } + _nextEnabled = isNextEnabled(); + _next->setIconOverride(_nextEnabled ? nullptr : &st::calendarNextDisabled); + _next->setRippleColorOverride(_nextEnabled ? nullptr : &st::boxBg); + _next->setCursor(_nextEnabled ? style::cur_pointer : style::cur_default); + if (!_nextEnabled) { + _next->clearState(); + } } void CalendarBox::resizeEvent(QResizeEvent *e) { @@ -700,7 +772,9 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { e->ignore(); } else if (e->key() == Qt::Key_Home) { - _inner->selectBeginning(); + jump(_previous.data()); + } else if (e->key() == Qt::Key_End) { + jump(_next.data()); } else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) { goPreviousMonth(); } else if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down) { diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h index a5115ffce..7a2ee2e36 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -8,7 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/layers/box_content.h" +#include "ui/widgets/tooltip.h" #include "base/required.h" +#include "base/timer.h" namespace style { struct CalendarSizes; @@ -35,10 +37,10 @@ struct CalendarBoxArgs { const style::CalendarSizes &st = st::defaultCalendarSizes; QDate minDate; QDate maxDate; - bool hasBeginningButton = false; + bool allowsSelection = false; }; -class CalendarBox final : public BoxContent { +class CalendarBox final : public BoxContent, private AbstractTooltipShower { public: CalendarBox(QWidget*, CalendarBoxArgs &&args); ~CalendarBox(); @@ -60,6 +62,14 @@ private: void setExactScroll(); void processScroll(); + void showJumpTooltip(not_null<IconButton*> button); + void jumpAfterDelay(not_null<IconButton*> button); + void jump(QPointer<IconButton> button); + + QString tooltipText() const override; + QPoint tooltipPos() const override; + bool tooltipWindowActive() const override; + const style::CalendarSizes &_st; class Context; @@ -74,11 +84,17 @@ private: object_ptr<Title> _title; object_ptr<IconButton> _previous; object_ptr<IconButton> _next; + bool _previousEnabled = false; + bool _nextEnabled = false; Fn<void(QDate date)> _callback; FnMut<void(not_null<CalendarBox*>)> _finalize; bool _watchScroll = false; + QPointer<IconButton> _tooltipButton; + QPointer<IconButton> _jumpButton; + base::Timer _jumpTimer; + }; } // namespace Ui diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index d4454785d..891cae582 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1147,63 +1147,61 @@ void SessionController::startOrJoinGroupCall( } void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { + const auto history = chat.history(); + if (!history) { + return; + } const auto currentPeerDate = [&] { - if (const auto history = chat.history()) { - if (history->scrollTopItem) { - return history->scrollTopItem->dateTime().date(); - } else if (history->loadedAtTop() - && !history->isEmpty() - && history->peer->migrateFrom()) { - if (const auto migrated = history->owner().historyLoaded(history->peer->migrateFrom())) { - if (migrated->scrollTopItem) { - // We're up in the migrated history. - // So current date is the date of first message here. - return history->blocks.front()->messages.front()->dateTime().date(); - } + if (history->scrollTopItem) { + return history->scrollTopItem->dateTime().date(); + } else if (history->loadedAtTop() + && !history->isEmpty() + && history->peer->migrateFrom()) { + if (const auto migrated = history->owner().historyLoaded(history->peer->migrateFrom())) { + if (migrated->scrollTopItem) { + // We're up in the migrated history. + // So current date is the date of first message here. + return history->blocks.front()->messages.front()->dateTime().date(); } - } else if (const auto item = history->lastMessage()) { - return base::unixtime::parse(item->date()).date(); } + } else if (const auto item = history->lastMessage()) { + return base::unixtime::parse(item->date()).date(); } return QDate(); }(); - const auto maxPeerDate = [](Dialogs::Key chat) { - if (auto history = chat.history()) { - if (const auto channel = history->peer->migrateTo()) { - history = channel->owner().historyLoaded(channel); - } - if (const auto item = history ? history->lastMessage() : nullptr) { - return base::unixtime::parse(item->date()).date(); - } + const auto maxPeerDate = [&] { + const auto check = history->peer->migrateTo() + ? history->owner().historyLoaded(history->peer->migrateTo()) + : history; + if (const auto item = check ? check->lastMessage() : nullptr) { + return base::unixtime::parse(item->date()).date(); } - return QDate::currentDate(); - }; - const auto minPeerDate = [](Dialogs::Key chat) { + return QDate(); + }(); + const auto minPeerDate = [&] { const auto startDate = [] { // Telegram was launched in August 2013 :) return QDate(2013, 8, 1); }; - if (const auto history = chat.history()) { - if (const auto chat = history->peer->migrateFrom()) { - if (const auto history = chat->owner().historyLoaded(chat)) { - if (history->loadedAtTop()) { - if (!history->isEmpty()) { - return history->blocks.front()->messages.front()->dateTime().date(); - } - } else { - return startDate(); + if (const auto chat = history->peer->migrateFrom()) { + if (const auto history = chat->owner().historyLoaded(chat)) { + if (history->loadedAtTop()) { + if (!history->isEmpty()) { + return history->blocks.front()->messages.front()->dateTime().date(); } + } else { + return startDate(); } } - if (history->loadedAtTop()) { - if (!history->isEmpty()) { - return history->blocks.front()->messages.front()->dateTime().date(); - } - return QDate::currentDate(); - } + } + if (history->loadedAtTop()) { + if (!history->isEmpty()) { + return history->blocks.front()->messages.front()->dateTime().date(); + } + return QDate::currentDate(); } return startDate(); - }; + }(); const auto highlighted = !requestedDate.isNull() ? requestedDate : !currentPeerDate.isNull() @@ -1215,9 +1213,9 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { .callback = [=](const QDate &date) { session().api().jumpToDate(chat, date); }, - .minDate = minPeerDate(chat), - .maxDate = maxPeerDate(chat), - .hasBeginningButton = true, + .minDate = minPeerDate, + .maxDate = maxPeerDate, + .allowsSelection = history->peer->isUser(), })); } From b7c95e9636e83751c315ee9c2463afc81189413e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Nov 2021 09:52:13 +0400 Subject: [PATCH 050/180] Support selecting messages range. --- .../SourceFiles/ui/boxes/calendar_box.cpp | 294 +++++++++++++++--- Telegram/SourceFiles/ui/boxes/calendar_box.h | 11 + .../window/window_session_controller.cpp | 48 +++ 3 files changed, 314 insertions(+), 39 deletions(-) diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 5673eb42f..10286c1b0 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -28,9 +28,9 @@ class CalendarBox::Context { public: Context(QDate month, QDate highlighted); - void setHasSelection(bool has); - [[nodiscard]] bool hasSelection() const { - return _hasSelection; + void setAllowsSelection(bool allowsSelection); + [[nodiscard]] bool allowsSelection() const { + return _allowsSelection; } void setMinDate(QDate date); @@ -66,20 +66,38 @@ public: return (index >= _minDayIndex) && (index <= _maxDayIndex); } + [[nodiscard]] QDate month() const { + return _month.current(); + } [[nodiscard]] rpl::producer<QDate> monthValue() const { return _month.value(); } - QDate dateFromIndex(int index) const; - QString labelFromIndex(int index) const; + [[nodiscard]] QDate dateFromIndex(int index) const; + [[nodiscard]] QString labelFromIndex(int index) const; + + void toggleSelectionMode(bool enabled); + [[nodiscard]] bool selectionMode() const; + [[nodiscard]] rpl::producer<> selectionUpdates() const; + [[nodiscard]] std::optional<int> selectedMin() const; + [[nodiscard]] std::optional<int> selectedMax() const; + + void startSelection(int index); + void updateSelection(int index); private: + struct Selection { + QDate min; + QDate max; + int minIndex = 0; + int maxIndex = 0; + }; void applyMonth(const QDate &month, bool forced = false); static int DaysShiftForMonth(QDate month, QDate min); static int RowsCountForMonth(QDate month, QDate min, QDate max); - bool _hasSelection = false; + bool _allowsSelection = false; rpl::variable<QDate> _month; QDate _min, _max; @@ -94,6 +112,12 @@ private: int _daysShift = 0; int _rowsCount = 0; + Selection _selection; + QDate _selectionStart; + int _selectionStartIndex = 0; + rpl::event_stream<> _selectionUpdates; + bool _selectionMode = false; + }; CalendarBox::Context::Context(QDate month, QDate highlighted) @@ -101,8 +125,8 @@ CalendarBox::Context::Context(QDate month, QDate highlighted) showMonth(month); } -void CalendarBox::Context::setHasSelection(bool has) { - _hasSelection = has; +void CalendarBox::Context::setAllowsSelection(bool allows) { + _allowsSelection = allows; } void CalendarBox::Context::setMinDate(QDate date) { @@ -128,17 +152,31 @@ bool CalendarBox::Context::showsMonthOf(QDate date) const { } void CalendarBox::Context::applyMonth(const QDate &month, bool forced) { + const auto was = _month.current(); _daysCount = month.daysInMonth(); _daysShift = DaysShiftForMonth(month, _min); _rowsCount = RowsCountForMonth(month, _min, _max); _highlightedIndex = month.daysTo(_highlighted); _minDayIndex = _min.isNull() ? INT_MIN : month.daysTo(_min); _maxDayIndex = _max.isNull() ? INT_MAX : month.daysTo(_max); + const auto shift = was.isNull() ? 0 : month.daysTo(was); + auto updated = false; + const auto update = [&](const QDate &date, int &index) { + if (shift && !date.isNull()) { + index += shift; + } + }; + update(_selection.min, _selection.minIndex); + update(_selection.max, _selection.maxIndex); + update(_selectionStart, _selectionStartIndex); if (forced) { _month.force_assign(month); } else { _month = month; } + if (updated) { + _selectionUpdates.fire({}); + } } void CalendarBox::Context::skipMonth(int skip) { @@ -235,6 +273,70 @@ QString CalendarBox::Context::labelFromIndex(int index) const { return QString::number(day()); } +void CalendarBox::Context::toggleSelectionMode(bool enabled) { + if (_selectionMode == enabled) { + return; + } + _selectionMode = enabled; + _selectionStart = {}; + _selection = {}; + _selectionUpdates.fire({}); +} + +bool CalendarBox::Context::selectionMode() const { + return _selectionMode; +} + +rpl::producer<> CalendarBox::Context::selectionUpdates() const { + return _selectionUpdates.events(); +} + +std::optional<int> CalendarBox::Context::selectedMin() const { + return _selection.min.isNull() + ? std::optional<int>() + : _selection.minIndex; +} + +std::optional<int> CalendarBox::Context::selectedMax() const { + return _selection.max.isNull() + ? std::optional<int>() + : _selection.maxIndex; +} + +void CalendarBox::Context::startSelection(int index) { + Expects(_selectionMode); + + if (!_selectionStart.isNull() && _selectionStartIndex == index) { + return; + } + _selectionStartIndex = index; + _selectionStart = dateFromIndex(index); + updateSelection(index); +} + +void CalendarBox::Context::updateSelection(int index) { + Expects(_selectionMode); + Expects(!_selectionStart.isNull()); + + index = std::clamp(index, minDayIndex(), maxDayIndex()); + const auto start = _selectionStartIndex; + const auto min = std::min(index, start); + const auto max = std::max(index, start); + if (!_selection.min.isNull() + && _selection.minIndex == min + && !_selection.max.isNull() + && _selection.maxIndex == max) { + return; + } + _selection = Selection{ + .min = dateFromIndex(min), + .max = dateFromIndex(max), + .minIndex = min, + .maxIndex = max, + }; + _selectionUpdates.fire({}); +} + class CalendarBox::Inner final : public RpWidget { public: Inner( @@ -294,6 +396,11 @@ CalendarBox::Inner::Inner( ) | rpl::start_with_next([=](QDate month) { monthChanged(month); }, lifetime()); + + context->selectionUpdates( + ) | rpl::start_with_next([=] { + update(); + }, lifetime()); } void CalendarBox::Inner::monthChanged(QDate month) { @@ -329,7 +436,13 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { p.setFont(st::calendarDaysFont); auto y = rowsTop(); auto index = -_context->daysShift(); - auto highlightedIndex = _context->highlightedIndex(); + const auto selectionMode = _context->selectionMode(); + const auto impossible = index - 45; + const auto selectedMin = _context->selectedMin().value_or(impossible); + const auto selectedMax = _context->selectedMax().value_or(impossible); + const auto highlightedIndex = selectionMode + ? impossible + : _context->highlightedIndex(); const auto daysCount = _context->daysCount(); const auto rowsCount = _context->rowsCount(); const auto rowHeight = _st.cellSize.height(); @@ -339,31 +452,59 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { rowsCount); y += fromRow * rowHeight; index += fromRow * kDaysInWeek; + const auto innerSkipLeft = (_st.cellSize.width() - _st.cellInner) / 2; + const auto innerSkipTop = (_st.cellSize.height() - _st.cellInner) / 2; for (auto row = fromRow; row != tillRow; ++row, y += rowHeight) { auto x = rowsLeft(); + const auto fromIndex = index; + const auto tillIndex = (index + kDaysInWeek); + const auto selectedFrom = std::max(fromIndex, selectedMin); + const auto selectedTill = std::min(tillIndex, selectedMax + 1); + const auto selectedInRow = (selectedTill - selectedFrom); + if (selectedInRow > 0) { + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(st::lightButtonBgOver); + p.drawRoundedRect( + (x + + (selectedFrom - index) * _st.cellSize.width() + + innerSkipLeft + - st::lineWidth), + y + innerSkipTop - st::lineWidth, + ((selectedInRow - 1) * _st.cellSize.width() + + 2 * st::lineWidth + + _st.cellInner), + _st.cellInner + 2 * st::lineWidth, + (_st.cellInner / 2.) + st::lineWidth, + (_st.cellInner / 2.) + st::lineWidth); + p.setBrush(Qt::NoBrush); + } for (auto col = 0; col != kDaysInWeek; ++col, ++index, x += _st.cellSize.width()) { - auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.cellSize.height()); - auto grayedOut = (index < 0 || index >= daysCount || !rect.intersects(clip)); - auto highlighted = (index == highlightedIndex); - auto enabled = _context->isEnabled(index); - auto innerLeft = x + (_st.cellSize.width() - _st.cellInner) / 2; - auto innerTop = y + (_st.cellSize.height() - _st.cellInner) / 2; + const auto rect = myrtlrect(x, y, _st.cellSize.width(), _st.cellSize.height()); + const auto selected = (index >= selectedMin) && (index <= selectedMax); + const auto grayedOut = !selected && (index < 0 || index >= daysCount); + const auto highlighted = (index == highlightedIndex); + const auto enabled = _context->isEnabled(index); + const auto innerLeft = x + innerSkipLeft; + const auto innerTop = y + innerSkipTop; if (highlighted) { - PainterHighQualityEnabler hq(p); + auto hq = PainterHighQualityEnabler(p); p.setPen(Qt::NoPen); p.setBrush(grayedOut ? st::windowBgOver : st::dialogsBgActive); p.drawEllipse(myrtlrect(innerLeft, innerTop, _st.cellInner, _st.cellInner)); p.setBrush(Qt::NoBrush); } - auto it = _ripples.find(index); + const auto it = _ripples.find(index); if (it != _ripples.cend()) { - auto colorOverride = [highlighted, grayedOut] { - if (highlighted) { + const auto colorOverride = [&] { + if (selectionMode) { + return st::lightButtonBgOver; + } else if (highlighted) { return grayedOut ? st::windowBgRipple : st::dialogsRippleBgActive; } return st::windowBgOver; - }; - it->second->paint(p, innerLeft, innerTop, width(), &(colorOverride()->c)); + }()->c; + it->second->paint(p, innerLeft, innerTop, width(), &colorOverride); if (it->second->empty()) { _ripples.erase(it); } @@ -400,6 +541,20 @@ void CalendarBox::Inner::mouseMoveEvent(QMouseEvent *e) { } else { setSelected(kEmptySelection); } + if (_pressed != kEmptySelection && _context->selectionMode()) { + const auto row = (point.y() >= rowsTop()) + ? (point.y() - rowsTop()) / size.height() + : -1; + const auto col = (point.y() < rowsTop()) + ? 0 + : (point.x() >= rowsLeft()) + ? std::min( + (point.x() - rowsLeft()) / size.width(), + kDaysInWeek - 1) + : 0; + const auto index = row * kDaysInWeek + col - _context->daysShift(); + _context->updateSelection(index); + } } void CalendarBox::Inner::setSelected(int selected) { @@ -440,13 +595,24 @@ void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) { } auto ripplePosition = QPoint(cell.x() + (_st.cellSize.width() - _st.cellInner) / 2, cell.y() + (_st.cellSize.height() - _st.cellInner) / 2); it->second->add(e->pos() - ripplePosition); + + if (_context->selectionMode()) { + if (_context->selectedMin().has_value() + && (e->modifiers() & Qt::ShiftModifier)) { + _context->updateSelection(_selected); + } else { + _context->startSelection(_selected); + } + } } } void CalendarBox::Inner::mouseReleaseEvent(QMouseEvent *e) { auto pressed = _pressed; setPressed(kEmptySelection); - if (pressed != kEmptySelection && pressed == _selected) { + if (pressed != kEmptySelection + && pressed == _selected + && !_context->selectionMode()) { crl::on_main(this, [=] { const auto onstack = _dateChosenCallback; onstack(_context->dateFromIndex(pressed)); @@ -490,7 +656,8 @@ protected: void paintEvent(QPaintEvent *e); private: - void monthChanged(QDate month); + void setTextFromMonth(QDate month); + void setText(QString text); void paintDayNames(Painter &p, QRect clip); const style::CalendarSizes &_st; @@ -513,13 +680,30 @@ CalendarBox::Title::Title( _textLeft = _st.padding.left() + (_st.cellSize.width() - dayWidth) / 2; _context->monthValue( - ) | rpl::start_with_next([=](QDate date) { - monthChanged(date); + ) | rpl::filter([=] { + return !_context->selectionMode(); + }) | rpl::start_with_next([=](QDate date) { + setTextFromMonth(date); + }, lifetime()); + + _context->selectionUpdates( + ) | rpl::start_with_next([=] { + if (!_context->selectionMode()) { + setTextFromMonth(_context->month()); + } else if (!_context->selectedMin()) { + setText(tr::lng_calendar_select_days(tr::now)); + } else { + setText(QString::number(1 + *_context->selectedMax() - *_context->selectedMin())); // #TODO calendar + } }, lifetime()); } -void CalendarBox::Title::monthChanged(QDate month) { - _text = langMonthOfYearFull(month.month(), month.year()); +void CalendarBox::Title::setTextFromMonth(QDate month) { + setText(langMonthOfYearFull(month.month(), month.year())); +} + +void CalendarBox::Title::setText(QString text) { + _text = std::move(text); _textWidth = st::calendarTitleFont->width(_text); update(); } @@ -570,9 +754,10 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args) , _next(this, st::calendarNext) , _callback(std::move(args.callback.value())) , _finalize(std::move(args.finalize)) -, _jumpTimer([=] { jump(_jumpButton); }) { +, _jumpTimer([=] { jump(_jumpButton); }) +, _selectionChanged(std::move(args.selectionChanged)) { _title->setAttribute(Qt::WA_TransparentForMouseEvents); - _context->setHasSelection(args.allowsSelection); + _context->setAllowsSelection(args.allowsSelection); _context->setMinDate(args.minDate); _context->setMaxDate(args.maxDate); @@ -612,6 +797,12 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args) setupJumps(_next.data(), &_nextEnabled); } +CalendarBox::~CalendarBox() = default; + +void CalendarBox::toggleSelectionMode(bool enabled) { + _context->toggleSelectionMode(enabled); +} + void CalendarBox::showJumpTooltip(not_null<IconButton*> button) { _tooltipButton = button; Ui::Tooltip::Show(kTooltipDelay, this); @@ -644,21 +835,33 @@ void CalendarBox::prepare() { _inner->setDateChosenCallback(std::move(_callback)); - addButton(tr::lng_close(), [=] { closeBox(); }); - _context->monthValue( ) | rpl::start_with_next([=](QDate month) { monthChanged(month); }, lifetime()); setExactScroll(); + _context->selectionUpdates( + ) | rpl::start_with_next([=] { + _selectionMode = _context->selectionMode(); + if (_selectionChanged) { + const auto count = !_selectionMode + ? std::optional<int>() + : !_context->selectedMin() + ? 0 + : (1 + *_context->selectedMax() - *_context->selectedMin()); + _selectionChanged(this, count); + } + if (!_selectionMode) { + clearButtons(); + createButtons(); + } + }, lifetime()); + createButtons(); + if (_finalize) { _finalize(this); } - if (_context->hasSelection()) { - addLeftButton(tr::lng_calendar_select_days(), [=] { - }); - } } bool CalendarBox::isPreviousEnabled() const { @@ -714,6 +917,21 @@ void CalendarBox::processScroll() { _watchScroll = true; } +void CalendarBox::createButtons() { + if (!_context->allowsSelection()) { + addButton(tr::lng_close(), [=] { closeBox(); }); + } else if (!_context->selectionMode()) { + addButton(tr::lng_close(), [=] { closeBox(); }); + addLeftButton(tr::lng_calendar_select_days(), [=] { + _context->toggleSelectionMode(true); + }); + } else { + addButton(tr::lng_cancel(), [=] { + _context->toggleSelectionMode(false); + }); + } +} + QString CalendarBox::tooltipText() const { if (_tooltipButton == _previous.data()) { return tr::lng_calendar_start_tip(tr::now); @@ -737,14 +955,14 @@ void CalendarBox::monthChanged(QDate month) { _previousEnabled = isPreviousEnabled(); _previous->setIconOverride(_previousEnabled ? nullptr : &st::calendarPreviousDisabled); _previous->setRippleColorOverride(_previousEnabled ? nullptr : &st::boxBg); - _previous->setCursor(_previousEnabled ? style::cur_pointer : style::cur_default); + _previous->setPointerCursor(_previousEnabled); if (!_previousEnabled) { _previous->clearState(); } _nextEnabled = isNextEnabled(); _next->setIconOverride(_nextEnabled ? nullptr : &st::calendarNextDisabled); _next->setRippleColorOverride(_nextEnabled ? nullptr : &st::boxBg); - _next->setCursor(_nextEnabled ? style::cur_pointer : style::cur_default); + _next->setPointerCursor(_nextEnabled); if (!_nextEnabled) { _next->clearState(); } @@ -782,6 +1000,4 @@ void CalendarBox::keyPressEvent(QKeyEvent *e) { } } -CalendarBox::~CalendarBox() = default; - } // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h index 7a2ee2e36..1af15ae77 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -38,6 +38,9 @@ struct CalendarBoxArgs { QDate minDate; QDate maxDate; bool allowsSelection = false; + Fn<void( + not_null<Ui::CalendarBox*>, + std::optional<int>)> selectionChanged; }; class CalendarBox final : public BoxContent, private AbstractTooltipShower { @@ -45,6 +48,8 @@ public: CalendarBox(QWidget*, CalendarBoxArgs &&args); ~CalendarBox(); + void toggleSelectionMode(bool enabled); + protected: void prepare() override; @@ -61,6 +66,7 @@ private: void goNextMonth(); void setExactScroll(); void processScroll(); + void createButtons(); void showJumpTooltip(not_null<IconButton*> button); void jumpAfterDelay(not_null<IconButton*> button); @@ -95,6 +101,11 @@ private: QPointer<IconButton> _jumpButton; base::Timer _jumpTimer; + bool _selectionMode = false; + Fn<void( + not_null<Ui::CalendarBox*>, + std::optional<int>)> _selectionChanged; + }; } // namespace Ui diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 891cae582..e9cc8b59f 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1207,6 +1207,53 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { : !currentPeerDate.isNull() ? currentPeerDate : QDate::currentDate(); + struct ButtonState { + enum class Type { + None, + Disabled, + Active, + }; + Type type = Type::None; + style::complex_color disabledFg = style::complex_color([] { + auto result = st::attentionBoxButton.textFg->c; + result.setAlpha(result.alpha() / 2); + return result; + }); + style::RoundButton disabled = st::attentionBoxButton; + }; + const auto buttonState = std::make_shared<ButtonState>(); + buttonState->disabled.textFg + = buttonState->disabled.textFgOver + = buttonState->disabledFg.color(); + buttonState->disabled.ripple.color + = buttonState->disabled.textBgOver + = buttonState->disabled.textBg; + const auto selectionChanged = [=]( + not_null<Ui::CalendarBox*> box, + std::optional<int> selected) { + if (!selected.has_value()) { + buttonState->type = ButtonState::Type::None; + return; + } + const auto type = (*selected > 0) + ? ButtonState::Type::Active + : ButtonState::Type::Disabled; + if (buttonState->type == type) { + return; + } + buttonState->type = type; + box->clearButtons(); + box->addButton(tr::lng_cancel(), [=] { + box->toggleSelectionMode(false); + }); + auto text = tr::lng_profile_clear_history(); + const auto button = box->addLeftButton(std::move(text), [=] { + + }, (*selected > 0) ? st::attentionBoxButton : buttonState->disabled); + if (!*selected) { + button->setPointerCursor(false); + } + }; show(Box<Ui::CalendarBox>(Ui::CalendarBoxArgs{ .month = highlighted, .highlighted = highlighted, @@ -1216,6 +1263,7 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { .minDate = minPeerDate, .maxDate = maxPeerDate, .allowsSelection = history->peer->isUser(), + .selectionChanged = selectionChanged, })); } From aa0a9b2db9b0d71168cc3daea026cd050fc9be01 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Nov 2021 10:16:27 +0400 Subject: [PATCH 051/180] Update API scheme on layer 135. --- Telegram/Resources/tl/api.tl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index ad07b961d..f2a919806 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -538,9 +538,9 @@ webPagePending#c586da1c id:long date:int = WebPage; webPage#e89c45b2 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector<WebPageAttribute> = WebPage; webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage; -authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; +authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; -account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations; +account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector<Authorization> = account.Authorizations; account.password#185b184f flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes pending_reset_date:flags.5?int = account.Password; @@ -1393,6 +1393,8 @@ account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:Re account.resetPassword#9308ce1b = account.ResetPasswordResult; account.declinePasswordReset#4c9409f6 = Bool; account.getChatThemes#d638de89 hash:long = account.Themes; +account.setAuthorizationTTL#bf899aa0 authorization_ttl_days:int = Bool; +account.changeAuthorizationSettings#432910d5 hash:long encrypted_requests_disabled:Bool = Bool; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; users.getFullUser#ca30a5b1 id:InputUser = UserFull; From 80fcffcc4070e0f48d02c68862d761926b4a625d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Nov 2021 11:26:35 +0400 Subject: [PATCH 052/180] Delete history for specific days in private chats. --- Telegram/Resources/langs/lang.strings | 6 ++ .../SourceFiles/boxes/delete_messages_box.cpp | 88 ++++++++++++++----- .../SourceFiles/boxes/delete_messages_box.h | 7 ++ Telegram/SourceFiles/data/data_histories.cpp | 53 +++++++++++ Telegram/SourceFiles/data/data_histories.h | 11 +++ .../SourceFiles/dialogs/dialogs_widget.cpp | 6 +- Telegram/SourceFiles/dialogs/dialogs_widget.h | 2 +- Telegram/SourceFiles/history/history.cpp | 14 +++ Telegram/SourceFiles/history/history.h | 1 + .../SourceFiles/history/history_widget.cpp | 9 +- .../view/history_view_context_menu.cpp | 9 +- .../history/view/history_view_list_widget.cpp | 9 +- Telegram/SourceFiles/info/info_top_bar.cpp | 8 +- .../info/media/info_media_list_widget.cpp | 9 +- .../SourceFiles/ui/boxes/calendar_box.cpp | 15 +++- Telegram/SourceFiles/ui/boxes/calendar_box.h | 3 + .../window/window_session_controller.cpp | 18 +++- .../window/window_session_controller.h | 2 +- 18 files changed, 211 insertions(+), 59 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index a08ae7ded..4859d3065 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -81,6 +81,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_calendar_select_days" = "Select days"; "lng_calendar_start_tip" = "Press and hold to jump to the start."; "lng_calendar_end_tip" = "Press and hold to jump to the end."; +"lng_calendar_days#one" = "{count} day"; +"lng_calendar_days#other" = "{count} days"; "lng_box_ok" = "OK"; "lng_box_done" = "Done"; @@ -1134,6 +1136,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sure_delete_saved_messages" = "Are you sure, you want to delete all your saved messages?\n\nThis action cannot be undone."; "lng_no_clear_history_channel" = "In channels you can enable auto-delete for messages."; "lng_no_clear_history_group" = "In public groups you can enable auto-delete for messages."; +"lng_sure_delete_by_date_one" = "Are you sure you want to delete all messages for **{date}**?\n\nThis action cannot be undone."; +"lng_sure_delete_by_date_many" = "Are you sure you want to delete all messages for the **{days}**?\n\nThis action cannot be undone."; +"lng_sure_delete_selected_days#one" = "{count} selected day"; +"lng_sure_delete_selected_days#other" = "{count} selected days"; "lng_message_empty" = "Empty Message"; "lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the latest version in Settings, or install it from {link}"; diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.cpp b/Telegram/SourceFiles/boxes/delete_messages_box.cpp index b13edac73..f099b3bca 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/delete_messages_box.cpp @@ -54,6 +54,18 @@ DeleteMessagesBox::DeleteMessagesBox( Expects(!_ids.empty()); } +DeleteMessagesBox::DeleteMessagesBox( + QWidget*, + not_null<PeerData*> peer, + QDate firstDayToDelete, + QDate lastDayToDelete) +: _session(&peer->session()) +, _wipeHistoryPeer(peer) +, _wipeHistoryJustClear(true) +, _wipeHistoryFirstToDelete(firstDayToDelete) +, _wipeHistoryLastToDelete(lastDayToDelete) { +} + DeleteMessagesBox::DeleteMessagesBox( QWidget*, not_null<PeerData*> peer, @@ -73,7 +85,27 @@ void DeleteMessagesBox::prepare() { auto deleteStyle = &st::defaultBoxButton; auto canDelete = true; if (const auto peer = _wipeHistoryPeer) { - if (_wipeHistoryJustClear) { + if (!_wipeHistoryFirstToDelete.isNull()) { + details = (_wipeHistoryFirstToDelete + == _wipeHistoryLastToDelete) + ? tr::lng_sure_delete_by_date_one( + tr::now, + lt_date, + TextWithEntities{ + langDayOfMonthFull(_wipeHistoryFirstToDelete) }, + Ui::Text::RichLangValue) + : tr::lng_sure_delete_by_date_many( + tr::now, + lt_days, + tr::lng_sure_delete_selected_days( + tr::now, + lt_count, + _wipeHistoryFirstToDelete.daysTo( + _wipeHistoryLastToDelete) + 1, + Ui::Text::WithEntities), + Ui::Text::RichLangValue); + deleteStyle = &st::attentionBoxButton; + } else if (_wipeHistoryJustClear) { const auto isChannel = peer->isBroadcast(); const auto isPublicGroup = peer->isMegagroup() && peer->asChannel()->isPublic(); @@ -397,16 +429,41 @@ void DeleteMessagesBox::keyPressEvent(QKeyEvent *e) { void DeleteMessagesBox::deleteAndClear() { const auto revoke = _revoke ? _revoke->checked() : false; - if (const auto peer = _wipeHistoryPeer) { + const auto session = _session; + const auto invokeCallbackAndClose = [&] { + // deleteMessages can initiate closing of the current section, + // which will cause this box to be destroyed. + const auto weak = Ui::MakeWeak(this); + if (const auto callback = _deleteConfirmedCallback) { + callback(); + } + if (const auto strong = weak.data()) { + strong->closeBox(); + } + }; + if (!_wipeHistoryFirstToDelete.isNull()) { + const auto peer = _wipeHistoryPeer; + const auto firstDayToDelete = _wipeHistoryFirstToDelete; + const auto lastDayToDelete = _wipeHistoryLastToDelete; + + invokeCallbackAndClose(); + session->data().histories().deleteMessagesByDates( + session->data().history(peer), + firstDayToDelete, + lastDayToDelete, + revoke); + session->data().sendHistoryChangeNotifications(); + return; + } else if (const auto peer = _wipeHistoryPeer) { const auto justClear = _wipeHistoryJustClear; - closeBox(); + invokeCallbackAndClose(); if (justClear) { - peer->session().api().clearHistory(peer, revoke); + session->api().clearHistory(peer, revoke); } else { - for (const auto &controller : peer->session().windows()) { + for (const auto &controller : session->windows()) { if (controller->activeChatCurrent().peer() == peer) { - Ui::showChatsList(&peer->session()); + Ui::showChatsList(session); } } // Don't delete old history by default, @@ -415,7 +472,7 @@ void DeleteMessagesBox::deleteAndClear() { //if (const auto from = peer->migrateFrom()) { // peer->session().api().deleteConversation(from, false); //} - peer->session().api().deleteConversation(peer, revoke); + session->api().deleteConversation(peer, revoke); } return; } @@ -441,19 +498,8 @@ void DeleteMessagesBox::deleteAndClear() { } } - if (_deleteConfirmedCallback) { - _deleteConfirmedCallback(); - } - - // deleteMessages can initiate closing of the current section, - // which will cause this box to be destroyed. - const auto session = _session; - const auto weak = Ui::MakeWeak(this); - - session->data().histories().deleteMessages(_ids, revoke); - - if (const auto strong = weak.data()) { - strong->closeBox(); - } + const auto ids = _ids; + invokeCallbackAndClose(); + session->data().histories().deleteMessages(ids, revoke); session->data().sendHistoryChangeNotifications(); } diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.h b/Telegram/SourceFiles/boxes/delete_messages_box.h index 627f433df..38bf7d38f 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.h +++ b/Telegram/SourceFiles/boxes/delete_messages_box.h @@ -29,6 +29,11 @@ public: QWidget*, not_null<Main::Session*> session, MessageIdsList &&selected); + DeleteMessagesBox( + QWidget*, + not_null<PeerData*> peer, + QDate firstDayToDelete, + QDate lastDayToDelete); DeleteMessagesBox(QWidget*, not_null<PeerData*> peer, bool justClear); void setDeleteConfirmedCallback(Fn<void()> callback) { @@ -56,6 +61,8 @@ private: PeerData * const _wipeHistoryPeer = nullptr; const bool _wipeHistoryJustClear = false; + const QDate _wipeHistoryFirstToDelete; + const QDate _wipeHistoryLastToDelete; const MessageIdsList _ids; UserData *_moderateFrom = nullptr; ChannelData *_moderateInChannel = nullptr; diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 2af0634b7..21836dc17 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_folder.h" #include "data/data_scheduled_messages.h" +#include "base/unixtime.h" #include "main/main_session.h" #include "window/notifications_manager.h" #include "history/history.h" @@ -708,6 +709,58 @@ void Histories::deleteAllMessages( }); } +void Histories::deleteMessagesByDates( + not_null<History*> history, + QDate firstDayToDelete, + QDate lastDayToDelete, + bool revoke) { + const auto firstSecondToDelete = base::unixtime::serialize( + { firstDayToDelete, QTime(0, 0) } + ); + const auto lastSecondToDelete = base::unixtime::serialize( + { lastDayToDelete, QTime(23, 59, 59) } + ); + deleteMessagesByDates( + history, + firstSecondToDelete - 1, + lastSecondToDelete + 1, + revoke); +} + +void Histories::deleteMessagesByDates( + not_null<History*> history, + TimeId minDate, + TimeId maxDate, + bool revoke) { + sendRequest(history, RequestType::Delete, [=](Fn<void()> finish) { + const auto peer = history->peer; + const auto fail = [=](const MTP::Error &error) { + finish(); + }; + using Flag = MTPmessages_DeleteHistory::Flag; + const auto flags = Flag::f_just_clear + | Flag::f_min_date + | Flag::f_max_date + | (revoke ? Flag::f_revoke : Flag(0)); + return session().api().request(MTPmessages_DeleteHistory( + MTP_flags(flags), + peer->input, + MTP_int(0), + MTP_int(minDate), + MTP_int(maxDate) + )).done([=](const MTPmessages_AffectedHistory &result) { + const auto offset = session().api().applyAffectedHistory( + peer, + result); + if (offset > 0) { + deleteMessagesByDates(history, minDate, maxDate, revoke); + } + finish(); + }).fail(fail).send(); + }); + history->destroyMessagesByDates(minDate, maxDate); +} + void Histories::deleteMessages(const MessageIdsList &ids, bool revoke) { auto remove = std::vector<not_null<HistoryItem*>>(); remove.reserve(ids.size()); diff --git a/Telegram/SourceFiles/data/data_histories.h b/Telegram/SourceFiles/data/data_histories.h index 81e9fd1fa..6d36b2f48 100644 --- a/Telegram/SourceFiles/data/data_histories.h +++ b/Telegram/SourceFiles/data/data_histories.h @@ -71,6 +71,17 @@ public: bool justClear, bool revoke); + void deleteMessagesByDates( + not_null<History*> history, + QDate firstDayToDelete, + QDate lastDayToDelete, + bool revoke); + void deleteMessagesByDates( + not_null<History*> history, + TimeId minDate, + TimeId maxDate, + bool revoke); + void deleteMessages(const MessageIdsList &ids, bool revoke); int sendRequest( diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 8001a6627..74ccca11e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -284,7 +284,7 @@ Widget::Widget( }, lifetime()); _cancelSearch->setClickedCallback([this] { onCancelSearch(); }); - _jumpToDate->entity()->setClickedCallback([this] { showJumpToDate(); }); + _jumpToDate->entity()->setClickedCallback([this] { showCalendar(); }); _chooseFromUser->entity()->setClickedCallback([this] { showSearchFrom(); }); rpl::single( rpl::empty_value() @@ -1453,9 +1453,9 @@ void Widget::clearSearchCache() { cancelSearchRequest(); } -void Widget::showJumpToDate() { +void Widget::showCalendar() { if (_searchInChat) { - controller()->showJumpToDate(_searchInChat, QDate()); + controller()->showCalendar(_searchInChat, QDate()); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index 668fa5f8e..63a6c6650 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -150,7 +150,7 @@ private: void setupMainMenuToggle(); bool searchForPeersRequired(const QString &query) const; void setSearchInChat(Key chat, PeerData *from = nullptr); - void showJumpToDate(); + void showCalendar(); void showSearchFrom(); void showMainMenu(); void clearSearchCache(); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index b3fd77016..655415dc0 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -462,6 +462,20 @@ void History::destroyMessage(not_null<HistoryItem*> item) { } } +void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) { + auto toDestroy = std::vector<not_null<HistoryItem*>>(); + for (const auto &message : _messages) { + if (message->isRegular() + && message->date() > minDate + && message->date() < maxDate) { + toDestroy.push_back(message.get()); + } + } + for (const auto item : toDestroy) { + item->destroy(); + } +} + void History::unpinAllMessages() { session().storage().remove( Storage::SharedMediaRemoveAll( diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index e8c9cc962..9f2257a84 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -132,6 +132,7 @@ public: std::forward<Args>(args)...)).get()); } void destroyMessage(not_null<HistoryItem*> item); + void destroyMessagesByDates(TimeId minDate, TimeId maxDate); void unpinAllMessages(); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 2f9de3913..17cffd96e 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -6782,13 +6782,10 @@ void HistoryWidget::confirmDeleteSelected() { if (items.empty()) { return; } - const auto weak = Ui::MakeWeak(this); auto box = Box<DeleteMessagesBox>(&session(), std::move(items)); - box->setDeleteConfirmedCallback([=] { - if (const auto strong = weak.data()) { - strong->clearSelected(); - } - }); + box->setDeleteConfirmedCallback(crl::guard(this, [=] { + clearSelected(); + })); controller()->show(std::move(box)); } diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index e39bfa6d3..3b0b9bb27 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -689,16 +689,13 @@ bool AddDeleteSelectedAction( } menu->addAction(tr::lng_context_delete_selected(tr::now), [=] { - const auto weak = Ui::MakeWeak(list); auto items = ExtractIdsList(request.selectedItems); auto box = Box<DeleteMessagesBox>( &request.navigation->session(), std::move(items)); - box->setDeleteConfirmedCallback([=] { - if (const auto strong = weak.data()) { - strong->cancelSelection(); - } - }); + box->setDeleteConfirmedCallback(crl::guard(list, [=] { + list->cancelSelection(); + })); request.navigation->parentController()->show(std::move(box)); }); return true; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 7ea987b29..e7f3c1757 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -3027,15 +3027,12 @@ void ConfirmDeleteSelectedItems(not_null<ListWidget*> widget) { return; } } - const auto weak = Ui::MakeWeak(widget); auto box = Box<DeleteMessagesBox>( &widget->controller()->session(), widget->getSelectedIds()); - box->setDeleteConfirmedCallback([=] { - if (const auto strong = weak.data()) { - strong->cancelSelection(); - } - }); + box->setDeleteConfirmedCallback(crl::guard(widget, [=] { + widget->cancelSelection(); + })); widget->controller()->show(std::move(box)); } diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 4b39c82ef..fae562b0c 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -544,11 +544,9 @@ void TopBar::performDelete() { auto box = Box<DeleteMessagesBox>( &_navigation->session(), std::move(items)); - box->setDeleteConfirmedCallback([weak = Ui::MakeWeak(this)] { - if (weak) { - weak->_cancelSelectionClicks.fire({}); - } - }); + box->setDeleteConfirmedCallback(crl::guard(this, [=] { + _cancelSelectionClicks.fire({}); + })); _navigation->parentController()->show(std::move(box)); } } diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 5584b182f..7f3d46cee 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -1715,12 +1715,9 @@ void ListWidget::forwardItems(MessageIdsList &&items) { void ListWidget::deleteSelected() { if (const auto box = deleteItems(collectSelectedIds())) { - const auto weak = Ui::MakeWeak(this); - box->setDeleteConfirmedCallback([=]{ - if (const auto strong = weak.data()) { - strong->clearSelected(); - } - }); + box->setDeleteConfirmedCallback(crl::guard(this, [=]{ + clearSelected(); + })); } } diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 10286c1b0..7cc728044 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -693,7 +693,10 @@ CalendarBox::Title::Title( } else if (!_context->selectedMin()) { setText(tr::lng_calendar_select_days(tr::now)); } else { - setText(QString::number(1 + *_context->selectedMax() - *_context->selectedMin())); // #TODO calendar + setText(tr::lng_calendar_days( + tr::now, + lt_count, + (1 + *_context->selectedMax() - *_context->selectedMin()))); } }, lifetime()); } @@ -803,6 +806,16 @@ void CalendarBox::toggleSelectionMode(bool enabled) { _context->toggleSelectionMode(enabled); } +QDate CalendarBox::selectedFirstDate() const { + const auto min = _context->selectedMin(); + return min.has_value() ? _context->dateFromIndex(*min) : QDate(); +} + +QDate CalendarBox::selectedLastDate() const { + const auto max = _context->selectedMax(); + return max.has_value() ? _context->dateFromIndex(*max) : QDate(); +} + void CalendarBox::showJumpTooltip(not_null<IconButton*> button) { _tooltipButton = button; Ui::Tooltip::Show(kTooltipDelay, this); diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h index 1af15ae77..43bfa06ba 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -50,6 +50,9 @@ public: void toggleSelectionMode(bool enabled); + [[nodiscard]] QDate selectedFirstDate() const; + [[nodiscard]] QDate selectedLastDate() const; + protected: void prepare() override; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index e9cc8b59f..7c0c591ba 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/add_contact_box.h" #include "boxes/peers/edit_peer_info_box.h" #include "boxes/peer_list_controllers.h" +#include "boxes/delete_messages_box.h" #include "window/window_adaptive.h" #include "window/window_controller.h" #include "window/main_window.h" @@ -151,7 +152,7 @@ void DateClickHandler::setDate(QDate date) { void DateClickHandler::onClick(ClickContext context) const { const auto my = context.other.value<ClickHandlerContext>(); if (const auto window = my.sessionWindow.get()) { - window->showJumpToDate(_chat, _date); + window->showCalendar(_chat, _date); } } @@ -1146,7 +1147,7 @@ void SessionController::startOrJoinGroupCall( } } -void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { +void SessionController::showCalendar(Dialogs::Key chat, QDate requestedDate) { const auto history = chat.history(); if (!history) { return; @@ -1248,7 +1249,18 @@ void SessionController::showJumpToDate(Dialogs::Key chat, QDate requestedDate) { }); auto text = tr::lng_profile_clear_history(); const auto button = box->addLeftButton(std::move(text), [=] { - + const auto firstDate = box->selectedFirstDate(); + const auto lastDate = box->selectedLastDate(); + if (!firstDate.isNull()) { + auto confirm = Box<DeleteMessagesBox>( + history->peer, + firstDate, + lastDate); + confirm->setDeleteConfirmedCallback(crl::guard(box, [=] { + box->closeBox(); + })); + box->getDelegate()->show(std::move(confirm)); + } }, (*selected > 0) ? st::attentionBoxButton : buttonState->disabled); if (!*selected) { button->setPointerCursor(false); diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 68a9f5791..2cca76cdd 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -363,7 +363,7 @@ public: } void removeLayerBlackout(); - void showJumpToDate( + void showCalendar( Dialogs::Key chat, QDate requestedDate); From aab4dbb7cf3ec4bc3a092e971c9a0ec343027fbc Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Nov 2021 12:10:59 +0400 Subject: [PATCH 053/180] Hide export and fast share for noforwards. --- Telegram/SourceFiles/data/data_peer.cpp | 2 +- Telegram/SourceFiles/history/view/history_view_message.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 19ea59404..29d5f01b2 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -518,7 +518,7 @@ bool PeerData::canEditMessagesIndefinitely() const { } bool PeerData::canExportChatHistory() const { - if (isRepliesChat()) { + if (isRepliesChat() || !allowsForwarding()) { return false; } if (const auto channel = asChannel()) { diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index d6ebd1d6c..2840cd385 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2217,7 +2217,7 @@ std::optional<QSize> Message::rightActionSize() const { bool Message::displayFastShare() const { const auto item = message(); const auto peer = item->history()->peer; - if (!item->isRegular()) { + if (!item->isRegular() || !peer->allowsForwarding()) { return false; } else if (peer->isChannel()) { return !peer->isMegagroup(); From a51168111351434b78c63c4d120980f03ce4464f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Nov 2021 18:42:33 +0400 Subject: [PATCH 054/180] Take session name from device model. --- .../SourceFiles/api/api_authorizations.cpp | 22 +++++++++---------- Telegram/SourceFiles/api/api_authorizations.h | 2 +- Telegram/SourceFiles/boxes/boxes.style | 2 +- Telegram/SourceFiles/boxes/sessions_box.cpp | 13 +++-------- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index d8e997c36..2293c1e8a 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -46,12 +46,10 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { return version; }(); - result.name = QString("%1%2").arg( - appName, - appVer.isEmpty() ? QString() : (' ' + appVer)); + result.name = qs(data.vdevice_model()); const auto country = qs(data.vcountry()); - const auto platform = qs(data.vplatform()); + //const auto platform = qs(data.vplatform()); //const auto &countries = countriesByISO2(); //const auto j = countries.constFind(country); //if (j != countries.cend()) { @@ -61,14 +59,10 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { result.activeTime = data.vdate_active().v ? data.vdate_active().v : data.vdate_created().v; - result.info = QString("%1, %2%3").arg( - qs(data.vdevice_model()), - platform.isEmpty() ? QString() : platform + ' ', - qs(data.vsystem_version())); - result.ip = qs(data.vip()) - + (country.isEmpty() - ? QString() - : QString::fromUtf8(" \xe2\x80\x93 ") + country); + result.info = QString("%1%2").arg( + appName, + appVer.isEmpty() ? QString() : (' ' + appVer)); + result.ip = qs(data.vip()); if (!result.hash) { result.active = tr::lng_status_online(tr::now); } else { @@ -85,6 +79,10 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { result.active = lastDate.toString(cDateFormat()); } } + result.location = (country.isEmpty() ? result.ip : country) + + (result.hash + ? (QString::fromUtf8(" \xe2\x80\x93 ") + result.active) + : QString()); return result; } diff --git a/Telegram/SourceFiles/api/api_authorizations.h b/Telegram/SourceFiles/api/api_authorizations.h index 96e036199..f571402d0 100644 --- a/Telegram/SourceFiles/api/api_authorizations.h +++ b/Telegram/SourceFiles/api/api_authorizations.h @@ -22,7 +22,7 @@ public: bool incomplete = false; TimeId activeTime = 0; - QString name, active, info, ip; + QString name, active, info, ip, location; }; using List = std::vector<Entry>; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 3e519d40f..6ee4529a0 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -291,7 +291,7 @@ sessionWhenFont: msgDateFont; sessionWhenFg: windowSubTextFg; sessionInfoFont: msgFont; sessionInfoFg: windowSubTextFg; -sessionTerminateTop: 28px; +sessionTerminateTop: 9px; sessionTerminateSkip: 22px; sessionNamePadding: margins(0px, 0px, 5px, 0px); sessionTerminate: IconButton { diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 59d955b3d..0d4242a33 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -49,16 +49,15 @@ private: , incomplete(entry.incomplete) , activeTime(entry.activeTime) , name(st::sessionNameStyle, entry.name) - , active(st::sessionWhenStyle, entry.active) , info(st::sessionInfoStyle, entry.info) - , ip(st::sessionInfoStyle, entry.ip) { + , ip(st::sessionInfoStyle, entry.location) { }; uint64 hash = 0; bool incomplete = false; TimeId activeTime = 0; - Ui::Text::String name, active, info, ip; + Ui::Text::String name, info, ip; }; struct Full { Entry current; @@ -496,17 +495,11 @@ void SessionsContent::List::paintEvent(QPaintEvent *e) { for (auto i = from; i != till; ++i) { const auto &entry = _items[i]; - const auto activeW = entry.active.maxWidth(); - const auto nameW = available - - activeW - - st::sessionNamePadding.right(); + const auto nameW = _rowWidth.info; const auto nameH = entry.name.style()->font->height; const auto infoW = entry.hash ? _rowWidth.info : available; const auto infoH = entry.info.style()->font->height; - p.setPen(entry.hash ? st::sessionWhenFg : st::contactsStatusFgOnline); - entry.active.drawRight(p, xact, y, activeW, w); - p.setPen(st::sessionNameFg); entry.name.drawLeftElided(p, x, y, nameW, w); From f3a00abe1812f033b5f868825934896e07e24bed Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Nov 2021 20:26:05 +0400 Subject: [PATCH 055/180] Fix working with new tg_owt. --- Telegram/lib_webrtc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index 9d617f174..07f1f72e4 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit 9d617f17463d07ff45515800c8fd865939144413 +Subproject commit 07f1f72e45a75edb6304ba06c5e90fc287a1f0cb From 49544111abd7f4c39a446ecce9880dc8b79b72f8 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 16 Nov 2021 20:48:40 +0400 Subject: [PATCH 056/180] Allow setting custom device name. --- Telegram/Resources/langs/lang.strings | 3 + .../SourceFiles/api/api_authorizations.cpp | 18 ++++- Telegram/SourceFiles/api/api_authorizations.h | 1 + Telegram/SourceFiles/boxes/sessions_box.cpp | 72 ++++++++++++++++++- Telegram/SourceFiles/core/core_settings.cpp | 28 +++++++- Telegram/SourceFiles/core/core_settings.h | 17 +++++ Telegram/SourceFiles/mtproto/mtp_instance.cpp | 22 +++++- 7 files changed, 153 insertions(+), 8 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 4859d3065..df03deaf6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -699,6 +699,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_reset_sure" = "Are you sure you want to terminate\nall other sessions?"; "lng_settings_reset_one_sure" = "Do you want to terminate this session?"; "lng_settings_reset_button" = "Terminate"; +"lng_settings_rename_device" = "Rename"; +"lng_settings_device_name" = "Device name ({device})"; +"lng_settings_rename_device_title" = "Rename current device"; "lng_settings_manage_local_storage" = "Manage local storage"; "lng_settings_ask_question" = "Ask a Question"; "lng_settings_ask_sure" = "Please note that Telegram Support is run by volunteers. We try to respond as quickly as possible, but it may take a while.\n\nPlease take a look at the Telegram FAQ: it has important troubleshooting tips and answers to most questions."; diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index 2293c1e8a..c645cb8cd 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "base/unixtime.h" #include "core/changelogs.h" +#include "core/application.h" #include "lang/lang_keys.h" namespace Api { @@ -46,7 +47,9 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { return version; }(); - result.name = qs(data.vdevice_model()); + result.name = result.hash + ? qs(data.vdevice_model()) + : Core::App().settings().deviceModel(); const auto country = qs(data.vcountry()); //const auto platform = qs(data.vplatform()); @@ -91,6 +94,19 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { Authorizations::Authorizations(not_null<ApiWrap*> api) : _api(&api->instance()) { + Core::App().settings().deviceModelChanges( + ) | rpl::start_with_next([=](const QString &model) { + auto changed = false; + for (auto &entry : _list) { + if (!entry.hash) { + entry.name = model; + changed = true; + } + } + if (changed) { + _listChanges.fire({}); + } + }, _lifetime); } void Authorizations::reload() { diff --git a/Telegram/SourceFiles/api/api_authorizations.h b/Telegram/SourceFiles/api/api_authorizations.h index f571402d0..d84de7115 100644 --- a/Telegram/SourceFiles/api/api_authorizations.h +++ b/Telegram/SourceFiles/api/api_authorizations.h @@ -48,6 +48,7 @@ private: rpl::event_stream<> _listChanges; crl::time _lastReceived = 0; + rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 0d4242a33..3941c5802 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_authorizations.h" #include "base/timer.h" #include "base/unixtime.h" +#include "base/algorithm.h" +#include "base/platform/base_platform_info.h" #include "ui/boxes/confirm_box.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -19,15 +21,46 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_layers.h" #include "styles/style_settings.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" #include "ui/widgets/scroll_area.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" +#include "ui/layers/generic_box.h" +#include "core/application.h" +#include "core/core_settings.h" #include "window/window_session_controller.h" namespace { constexpr auto kSessionsShortPollTimeout = 60 * crl::time(1000); +constexpr auto kMaxDeviceModelLength = 16; + +void RenameBox(not_null<Ui::GenericBox*> box) { + box->setTitle(tr::lng_settings_rename_device_title()); + + const auto name = box->addRow(object_ptr<Ui::InputField>( + box, + st::defaultInputField, + tr::lng_settings_device_name( + lt_device, + rpl::single(Platform::DeviceModelPretty())), + Core::App().settings().customDeviceModel())); + name->setMaxLength(kMaxDeviceModelLength); + box->setFocusCallback([=] { + name->setFocusFast(); + }); + const auto submit = [=] { + const auto result = base::CleanAndSimplify( + name->getLastText()); + box->closeBox(); + Core::App().settings().setCustomDeviceModel(result); + Core::App().saveSettingsDelayed(); + }; + QObject::connect(name, &Ui::InputField::submitted, submit); + box->addButton(tr::lng_settings_save(), submit); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} } // namespace @@ -107,10 +140,11 @@ private: int available = 0; int info = 0; }; - RowWidth _rowWidth; + void subscribeToCustomDeviceModel(); void computeRowWidth(); + RowWidth _rowWidth; std::vector<Entry> _items; std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons; rpl::event_stream<uint64> _terminate; @@ -125,6 +159,7 @@ public: void showData(const Full &data); rpl::producer<uint64> terminateOne() const; rpl::producer<> terminateAll() const; + rpl::producer<> renameCurrentRequests() const; void terminatingOne(uint64 hash, bool terminating); @@ -312,7 +347,28 @@ void SessionsContent::Inner::setupContent() { const auto content = Ui::CreateChild<Ui::VerticalLayout>(this); - AddSubsectionTitle(content, tr::lng_sessions_header()); + const auto header = AddSubsectionTitle( + content, + tr::lng_sessions_header()); + const auto rename = Ui::CreateChild<Ui::LinkButton>( + content, + tr::lng_settings_rename_device(tr::now), + st::defaultLinkButton); + rpl::combine( + content->sizeValue(), + header->positionValue() + ) | rpl::start_with_next([=](QSize outer, QPoint position) { + const auto x = st::sessionTerminateSkip + + st::sessionTerminate.iconPosition.x(); + const auto y = st::settingsSubsectionTitlePadding.top() + + st::settingsSubsectionTitle.style.font->ascent + - st::defaultLinkButton.font->ascent; + rename->moveToRight(x, y, outer.width()); + }, rename->lifetime()); + rename->setClickedCallback([=] { + Ui::show(Box(RenameBox), Ui::LayerOption::KeepOther); + }); + _current = content->add(object_ptr<List>(content)); const auto terminateWrap = content->add( object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( @@ -400,6 +456,18 @@ void SessionsContent::List::resizeEvent(QResizeEvent *e) { computeRowWidth(); } +void SessionsContent::List::subscribeToCustomDeviceModel() { + Core::App().settings().deviceModelChanges( + ) | rpl::start_with_next([=](const QString &model) { + for (auto &entry : _items) { + if (!entry.hash) { + entry.name.setText(st::sessionNameStyle, model); + } + } + update(); + }, lifetime()); +} + void SessionsContent::List::showData(gsl::span<const Entry> items) { computeRowWidth(); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index a0124b6a6..05eeab254 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -124,7 +124,8 @@ QByteArray Settings::serialize() const { + Serialize::bytearraySize(proxy) + sizeof(qint32) * 2 + Serialize::bytearraySize(_photoEditorBrush) - + sizeof(qint32); + + sizeof(qint32) * 3 + + Serialize::stringSize(_customDeviceModel.current()); auto result = QByteArray(); result.reserve(size); @@ -221,7 +222,8 @@ QByteArray Settings::serialize() const { << _photoEditorBrush << qint32(_groupCallNoiseSuppression ? 1 : 0) << qint32(_voicePlaybackSpeed * 100) - << qint32(_closeToTaskbar.current() ? 1 : 0); + << qint32(_closeToTaskbar.current() ? 1 : 0) + << _customDeviceModel.current(); } return result; } @@ -305,6 +307,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 hiddenGroupCallTooltips = qint32(_hiddenGroupCallTooltips.value()); QByteArray photoEditorBrush = _photoEditorBrush; qint32 closeToTaskbar = _closeToTaskbar.current() ? 1 : 0; + QString customDeviceModel = _customDeviceModel.current(); stream >> themesAccentColors; if (!stream.atEnd()) { @@ -465,6 +468,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> closeToTaskbar; } + if (!stream.atEnd()) { + stream >> customDeviceModel; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -606,6 +612,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { }(); _photoEditorBrush = photoEditorBrush; _closeToTaskbar = (closeToTaskbar == 1); + _customDeviceModel = customDeviceModel; } QString Settings::getSoundPath(const QString &key) const { @@ -672,6 +679,23 @@ void Settings::setThirdColumnWidth(int width) { _thirdColumnWidth = width; } +QString Settings::deviceModel() const { + const auto custom = customDeviceModel(); + return custom.isEmpty() ? Platform::DeviceModelPretty() : custom; +} + +rpl::producer<QString> Settings::deviceModelChanges() const { + return customDeviceModelChanges() | rpl::map([=] { + return deviceModel(); + }); +} + +rpl::producer<QString> Settings::deviceModelValue() const { + return customDeviceModelValue() | rpl::map([=] { + return deviceModel(); + }); +} + int Settings::thirdColumnWidth() const { return _thirdColumnWidth.current(); } diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index f8a26f04d..8de40b6a3 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -614,6 +614,22 @@ public: return _closeToTaskbar.changes(); } + void setCustomDeviceModel(const QString &model) { + _customDeviceModel = model; + } + [[nodiscard]] QString customDeviceModel() const { + return _customDeviceModel.current(); + } + [[nodiscard]] rpl::producer<QString> customDeviceModelChanges() const { + return _customDeviceModel.changes(); + } + [[nodiscard]] rpl::producer<QString> customDeviceModelValue() const { + return _customDeviceModel.value(); + } + [[nodiscard]] QString deviceModel() const; + [[nodiscard]] rpl::producer<QString> deviceModelChanges() const; + [[nodiscard]] rpl::producer<QString> deviceModelValue() const; + [[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] static float64 DefaultDialogsWidthRatio(); [[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) { @@ -714,6 +730,7 @@ private: rpl::variable<WorkMode> _workMode = WorkMode::WindowAndTray; base::flags<Calls::Group::StickedTooltip> _hiddenGroupCallTooltips; rpl::variable<bool> _closeToTaskbar = false; + rpl::variable<QString> _customDeviceModel; bool _tabbedReplacedWithInfo = false; // per-window rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 3d8439d61..457a60a76 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -224,9 +224,12 @@ private: std::unique_ptr<QThread> _otherSessionsThread; std::vector<std::unique_ptr<QThread>> _fileSessionThreads; - QString _deviceModel; + QString _deviceModelDefault; QString _systemVersion; + mutable QMutex _deviceModelMutex; + QString _customDeviceModel; + rpl::variable<DcId> _mainDcId = Fields::kDefaultMainDc; bool _mainDcIdForced = false; base::flat_map<DcId, std::unique_ptr<Dcenter>> _dcenters; @@ -321,9 +324,19 @@ Instance::Private::Private( restart(); }, _lifetime); - _deviceModel = std::move(fields.deviceModel); + _deviceModelDefault = std::move(fields.deviceModel); _systemVersion = std::move(fields.systemVersion); + _customDeviceModel = Core::App().settings().customDeviceModel(); + Core::App().settings().customDeviceModelChanges( + ) | rpl::start_with_next([=](const QString &value) { + QMutexLocker lock(&_deviceModelMutex); + _customDeviceModel = value; + lock.unlock(); + + reInitConnection(mainDcId()); + }, _lifetime); + for (auto &key : fields.keys) { auto dcId = key->dcId(); auto shiftedDcId = dcId; @@ -874,7 +887,10 @@ bool Instance::Private::isTestMode() const { } QString Instance::Private::deviceModel() const { - return _deviceModel; + QMutexLocker lock(&_deviceModelMutex); + return _customDeviceModel.isEmpty() + ? _deviceModelDefault + : _customDeviceModel; } QString Instance::Private::systemVersion() const { From 395100584f485b075987def492b299dd86381436 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 18 Nov 2021 21:02:39 +0400 Subject: [PATCH 057/180] Add a second dropdown with playback mode controls. --- Telegram/CMakeLists.txt | 2 + .../icons/player/player_repeat_one.png | Bin 0 -> 262 bytes .../icons/player/player_repeat_one@2x.png | Bin 0 -> 304 bytes .../icons/player/player_repeat_one@3x.png | Bin 0 -> 377 bytes .../icons/player/player_repeat_reverse.png | Bin 0 -> 246 bytes .../icons/player/player_repeat_reverse@2x.png | Bin 0 -> 301 bytes .../icons/player/player_repeat_reverse@3x.png | Bin 0 -> 371 bytes .../icons/player/player_repeat_shuffle.png | Bin 0 -> 269 bytes .../icons/player/player_repeat_shuffle@2x.png | Bin 0 -> 290 bytes .../icons/player/player_repeat_shuffle@3x.png | Bin 0 -> 387 bytes .../Resources/icons/player/player_reverse.png | Bin 0 -> 198 bytes .../icons/player/player_reverse@2x.png | Bin 0 -> 212 bytes .../icons/player/player_reverse@3x.png | Bin 0 -> 298 bytes .../Resources/icons/player/player_shuffle.png | Bin 0 -> 346 bytes .../icons/player/player_shuffle@2x.png | Bin 0 -> 537 bytes .../icons/player/player_shuffle@3x.png | Bin 0 -> 761 bytes Telegram/SourceFiles/mainwidget.cpp | 40 ++++++++++++- Telegram/SourceFiles/mainwidget.h | 1 + .../media/player/media_player.style | 36 +++++++++++- .../player/media_player_repeat_controls.cpp | 53 ++++++++++++++++++ .../player/media_player_repeat_controls.h | 22 ++++++++ .../media/player/media_player_widget.cpp | 11 ++++ .../media/player/media_player_widget.h | 3 + 23 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 Telegram/Resources/icons/player/player_repeat_one.png create mode 100644 Telegram/Resources/icons/player/player_repeat_one@2x.png create mode 100644 Telegram/Resources/icons/player/player_repeat_one@3x.png create mode 100644 Telegram/Resources/icons/player/player_repeat_reverse.png create mode 100644 Telegram/Resources/icons/player/player_repeat_reverse@2x.png create mode 100644 Telegram/Resources/icons/player/player_repeat_reverse@3x.png create mode 100644 Telegram/Resources/icons/player/player_repeat_shuffle.png create mode 100644 Telegram/Resources/icons/player/player_repeat_shuffle@2x.png create mode 100644 Telegram/Resources/icons/player/player_repeat_shuffle@3x.png create mode 100644 Telegram/Resources/icons/player/player_reverse.png create mode 100644 Telegram/Resources/icons/player/player_reverse@2x.png create mode 100644 Telegram/Resources/icons/player/player_reverse@3x.png create mode 100644 Telegram/Resources/icons/player/player_shuffle.png create mode 100644 Telegram/Resources/icons/player/player_shuffle@2x.png create mode 100644 Telegram/Resources/icons/player/player_shuffle@3x.png create mode 100644 Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp create mode 100644 Telegram/SourceFiles/media/player/media_player_repeat_controls.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index bfd931857..d0605f9c9 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -813,6 +813,8 @@ PRIVATE media/player/media_player_instance.h media/player/media_player_panel.cpp media/player/media_player_panel.h + media/player/media_player_repeat_controls.cpp + media/player/media_player_repeat_controls.h media/player/media_player_volume_controller.cpp media/player/media_player_volume_controller.h media/player/media_player_widget.cpp diff --git a/Telegram/Resources/icons/player/player_repeat_one.png b/Telegram/Resources/icons/player/player_repeat_one.png new file mode 100644 index 0000000000000000000000000000000000000000..8020d6df31cf78f3e45515d2ca74246ec4b55ba7 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Z8+Vb&Z8px_Qq z7sn8diM^A2xmpZ(+~VED<rbK0e$k0LrMxS+AXq@~m0F6}qCWeQnhCC#rs)3-V%Xg_ zbMB{I>(=Jp-uv$%V`!?ay+qqXk2n0Yqb{4ItA?r`^XMrIKlPC@VjAmnnRAvdEsHc> z{FW2EDSf=qrr+E*_*zHwk|{bZ6Y_s?zTf+BZ+)#|OM<5A<F6hGpKWH(T>Gvxc6#Y+ zubsMu(J#_|3x|d>DwM_k<!<?EyFx29a-TH6gG*k$?s4%OH;%{7s-Kzw^aF#ZtDnm{ Hr-UW|?xbYO literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_one@2x.png b/Telegram/Resources/icons/player/player_repeat_one@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cd137b24ee3361c3a6e797632f2ffa0249286e82 GIT binary patch literal 304 zcmV-00nh%4P)<h;3K|Lk000e1NJLTq000^Q000~a1^@s6e-p3M00009a7bBm000XU z000XU0RWnu7ytkO;z>k7R7i=vmB9^zFbqXs5MnT<U<YR6W_Dl-4lLvzC~XO-v4fH? z<z!ng--;aowH#G7<@J59%d+U9pO_t#f(^r<v5r`SQ+w^b)^%muHl}I1uk|>xI2*_D zlGpybVaizu!JO}IT@0%N^5_xB)4JAL9Jk2>flNnU2td&2swqb~5O6xmg<L@MJQsn8 z$amyL#Izy2M@04wQ3Il?N)3prYIp}u^6s%a#dCLgb-7D6r4y(W<OC`S0SG&zECisb z0fCzT(bH#a)n|^L{x>OoT36ax1JHF{aagex6zc`cm@~3ytAuL+0000<MNUMnLSTZ{ C$az@+ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_one@3x.png b/Telegram/Resources/icons/player/player_repeat_one@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ec01cba5c58d18afe5bd9dc1e488891304fca860 GIT binary patch literal 377 zcmV-<0fzpGP)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkPD@jB_R9J=0*TId1FboAyXJ%?i9f-!_LMSB1v_LQ1|6F#_WJt0b z6NA6x3xV*2<wy__L0TaM@v}q(?>&}f!M1Jq&DTBh%>jsj-h0FtQOZ!96j;k}oCLV8 zD`Je8=UE@u3-sO>_ff-*XmPeyZT^Y$0x!k&0x!q)0Bg7hDaJW>ZUe2a#>_U`+5;2e zdN-BAF2hM+m*AwZW{tq9z((NIU}tfvuo_PNrT?f9!VzZ$1|Y{-f@L^supMU+1^{Fg z_H$zV{h&6kmL_hW8f&<0YAoRnQ)2~JoEi(b#?;)4tHJIJS%m=rS%m?><Mh9!VxB1_ zcFxf>O?O)kc@hz)l*lS@A8BST_MViFl1AkhN<cHX63`Ux8)y#q3A_vUqDGjxwXB{u Xmg(wRa>R1v00000NkvXXu0mjf7TulJ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_reverse.png b/Telegram/Resources/icons/player/player_repeat_reverse.png new file mode 100644 index 0000000000000000000000000000000000000000..6928cf64deca42f1e897d08150df566ff38ef675 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Z8+Vb&Z8px_Em z7sn8diM^8!ave6{ah~ta_ji87|NqHVXP2@R8QogA<nFDKZifjAZg`Y2m@L}uJ?Tzt zgG%kI^Q^Le?|t1ee}7`y49Q6bn`i%H>frFwoD!7Be`oH!2%&fTX0P4KS6u3Sy~g9x zw%o&u+_zmj$0vNovU|?wlE&Sux0dz&+0?F>y>`+P@dLZoZD0BK(8lE*4wnkv$e!NB tkRX-&oxjJHU(nGj^PF5k&CJL>j7PeTX7~BW$^hNW;OXk;vd$@?2>>rDWs(2@ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_reverse@2x.png b/Telegram/Resources/icons/player/player_repeat_reverse@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6aa3d20bfdedcec0b26fdfa3904c4faf45f37047 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)j!3HGjh1XvOQk(@Ik;M!Q+`=Ht$S`Y;1W@q4 zr;B5VN9WNgu|mxX0?o~rKTeU;pJ!YfutvxGuV%LK;bw&zsZFyPILuvy7z})a4fS=8 zFP*Z=c9)fhR(HzAX;0tm=Kq)w>mYaJP}G7YIj<VmJYTyz<1Bx=g6$Pn7okZT%+>9m zmmJ!7TeRnS**^^x(Gzkvr0)H`pY;D#V(hhj6}gJxD?e-PYX3KRSLHls1r_`4S^Dd* zpI|&FEU=bUlyBmaEqWr8z7+q;;a(e5zUg>e7-LtH$N~=zh9#|n+>8wmIXOP0aCc=b x3X7JF<gTtikQV!8J}-koU$TYH8rI0Yj5eAVqOTr`;Rkw^!PC{xWt~$(69D)+acKYm literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_reverse@3x.png b/Telegram/Resources/icons/player/player_repeat_reverse@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2981d37eb7de91c9cf51897d2cb48e68797b61e2 GIT binary patch literal 371 zcmV-(0gV2MP)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkPB}qg<R9J=W*g+D5APfc2Pwm0H8S!S^?03=XKqW!#2wC*cumNgc zXH3Kb0Ph$gVy@>g_T2*`X#$7fR@fR1*d6x`vw&T38cYLr!?j>F;1OK-EM7_op*o*m z@|Qi2tFRvV;{ZTJKyn*-@_7*{7A^<MjjZiCThU!{7O>i^&wlZd(Ynsjr^pao4m1x4 zqi`$GT34H}));%{-{-ETid&YYw_}$W<Gu;EZqgnj#`rEKudo8=4YuJt!pQe2z$I+$ zzs(QcjWeE7?O)^$S5%EB+^%Z8;Hp*Q0cTXrSeynM8F+<}B)q~%^5Nmct7S?lNeBV+ zJdd{QG69g361)PplGa{$adRs%MmbRfy1~_eu5jN#ceorl3ipJO;=i&1c>(y1#()`u Rp*sKo002ovPDHLkV1h45oEZQB literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_shuffle.png b/Telegram/Resources/icons/player/player_repeat_shuffle.png new file mode 100644 index 0000000000000000000000000000000000000000..dcb08429b7f807be4ae6cae2a9462eb3d23264fc GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Z8+Vb&Z8px}N_ z7sn8diBl&X<Z3n$aJetWfA8;<1JC~V|8uwDQfi!Y=*tAHEZtmown@HiC*QC+l$Vt6 z{GF-q`A)A|^uEH@q8`D9LYeQI+f~-F7#N?)5uYJ>*<|Z3m8(&a(^wl4B@B%3)QZd8 zP3lqbw0yqZM|xY1yMpKIUH8l$pPCXBJw3=T_SZ3iCJx0vItArZgN_&;HV-@(Iq3_7 zhvwbhSIWC`^|pqo7@q%p{B-So<}H?gQ<)`n`L~AcTKD^H<%NA`?A!c}FP~=naU-q( P=oJP}S3j3^P6<r_sEBFZ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_shuffle@2x.png b/Telegram/Resources/icons/player/player_repeat_shuffle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0aa1c6aee9c255045c2c65e2071c988442a1c0c6 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)j!3HGjh1XvOQk(@Ik;M!Q+`=Ht$S`Y;1W@pb zr;B5VN9W#Y-h77@cv#zYivBxWJg<q};?_1v%f!Op>D2MG9CjZrg-I%%L0k;cVk(Ci zxF$>PTzp#jUn(=V(JOUX$G*o5u_Eu!%uY<3q+%?bxT@m)_3aLP(vFXIt1d9^u#abZ zrFe1M$GM$0R_>NZ_TMzPuzSJ2808r=&o-yI345r$D7pGIHG->#DM;Me%w<vqlalA* zcWVp!{%Dt;{kppPJKO3j4Vx5rHH4Yw3!6zZZLno*WUuqndhyO^->O~xzl7HDEtsZf my_q-R{`ZoIDDGEZnKqt1b#?LlXFGuYWAJqKb6Mw<&;$U@>1<#C literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_shuffle@3x.png b/Telegram/Resources/icons/player/player_repeat_shuffle@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..5fe95448e4d3c76d4feeec8eb1e8fcaad3eca04b GIT binary patch literal 387 zcmV-}0et?6P)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkPHAzH4R9J=0*jo~VAPfc2P`fa9W|qdzZa-QWk?^oBa5Ix1M(~Iv z)9OS7LJ$DNIhTw*7Fo;y(tV=BiKM{4;Q*Mas>L~~8m}t>@sF&9s%F=#YR{bUxGS&| zhW4tJ=s^caxZxRg23uppaRF3=1zh&}XW&|}^8-xBwP0cArsGO5>$PTUj>O$L{N}p9 z>`HGdxzk3H`;W$b(}3f=k+{;~ZvmT4_%80(G#`iw*WH9?<GP!0?X1ko<2Z603k*Pt zV}d0(Hdw?l!hrX0d`j4-W3(rZ#+9Oqi&evhOR9zmmsJf5&a4^+T%~H(;!3cUA*?U} z5LOrfJgy(?m}2wZt8-5FeP3<KViOVd-U}-*mUQVKkqmAnl}raLAPvp}QsJ&ZI@}q! h3ip&s)V_qzh!<X$y(}q1<iG#`002ovPDHLkV1h(frlbG> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_reverse.png b/Telegram/Resources/icons/player/player_reverse.png new file mode 100644 index 0000000000000000000000000000000000000000..dea0be2df5352d1acdf63a3f443e6232ff7f30d5 GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Z8+Vb&Z8pkRfk zi(`n!#HW)C`Hm>?upBR}b2$0Go^9?=#%b$2EDoxAUApffqMcpvkd;G$P5J!!Gt+J! zOEl0}`r(L};0|kscYfz8e?IAob&<FoGIe6U`PqeAziG@_vi;B6DM3~Gt}LHFR=;;S uvR6gvN>pHI!_y!456!xnnpl7O596-o(R{ZrDYOCIz~JfX=d#Wzp$Py;D@-f^ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_reverse@2x.png b/Telegram/Resources/icons/player/player_reverse@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d3af5baf60257095948951257bb026701502a5cf GIT binary patch literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)j!3HGjh1XvOQk(@Ik;M!Q+`=Ht$S`Y;1W>Te z)5S5wqx0>wgIo-X94_f9zy90j@p}sdtxlcJ`g}$ozpepOMnKkETcwrG-lhK2zi}~K ziCWH<+t(ps6(#zu<k_ni+(}3G2<0!-@G0YRNpL-XY1Y@3NBnj!W>M8}<n+2A@A&je zhOVQm{kx2G#YsKA=a^qxKD7ST)Voo;Kanl^-|M_XX%E<|?(6&6T54|xx`@Hk)z4*} HQ$iB}pTbFD literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_reverse@3x.png b/Telegram/Resources/icons/player/player_reverse@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9b01d7a1bb2c8395bfcdca61bd465e1262d3e2e2 GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^>Oic;!3HD^Y@J>MDb50q$YKTtZeb8+WSBKa0w{RL z)5S3)qV?^~-F(c7JT8I4Pxt5UyQ8Tj)pAfyZt8lMsdwL86w_2|?U<t0$+1%FYE=64 zok6nptEME~PnH$DC}uqSdS(CBC-Y3-h<xD4W3gtubL?!w?gP4W*tapa6bedkwG?ph zFt!z1Z_qI4(ti8(tLBI1om#)<%Kz}>_mYWvbU~<nn#GnQ3sw1a3o|;My&*zntk>C& z|BL8dm-_5~`eNaEd<|PqZB)2(aN&WxTRYmjnb>Eaj-4Z((c}2NQ2Rm3(yHdo4c$NA q$gX2wSGHH**3YG*Bc^`OABL-p=F#^%U)KZu$>8bg=d#Wzp$P!J{dKqi literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_shuffle.png b/Telegram/Resources/icons/player/player_shuffle.png new file mode 100644 index 0000000000000000000000000000000000000000..662958889faad7045dd8bb1f87b9dd59887c0573 GIT binary patch literal 346 zcmV-g0j2(lP)<h;3K|Lk000e1NJLTq000dD000gM1^@s6^naGp00009a7bBm000XU z000XU0RWnu7ytkP3`s;mR49>+QZbH$Fc6#tiH?dNxHA%bfV?GN;Vl#hrSVg6$p<KL z<t}73DIz#>ce!IEOS5awtafApK%4+chzI~6IU@i7RR#I-7j6vLosRCmAqgQMgn$?$ zy!SB1z!-yZ91&wg2w|V}W(a`JIg(`4H0-*LB<Y;vc2AP?Ja6@@S=TkEX`;23>$*}^ zSyk1WXyZ5#UM?5*eNU3S-EPN6-_KW3mL*k{s!D6^N6}m4ZQCMA5)8wDqA31Ma^Fc+ zxvnd%wVbBu$4*-ivMdV$AkTATS%$9bAR@S4uejguh%url3aBd5G~LJU@ZQrHLt_lb sapWh+md1a0JRV5X6e5DtqT`2s0vXNm;~r$=IRF3v07*qoM6N<$f?ec_)&Kwi literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_shuffle@2x.png b/Telegram/Resources/icons/player/player_shuffle@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..54d44448fa27691cb47ba88316ef7f598f20cf47 GIT binary patch literal 537 zcmV+!0_OdRP)<h;3K|Lk000e1NJLTq000^Q000~a1^@s6e-p3M00009a7bBm000XU z000XU0RWnu7ytkP%Sl8*R7i=vma(paFcgMQ@8D7bk%2@923_Fk97$Mo(LrRwiH)bh zmw^yS2vNv?2QP$Ty<&p?Ne4>)e&?K?QWz0|CKLc-j6KZ%ObO}3z5<fLJ_Z`X7-V_& zB&+vukym1jz0N3(W8$25&zy6L<9IZr3koCTI1cFEop2myjO9V2KuMB7RaK~}iX=(i zhY;b1F-F5-+cq^#)1@8+0p)p4o6UxFUGL_brXkz51+Q#>x)RDp6h#1lG)<vt8s_sk zvMduKjiTtmH_W6r<e=4Rh3oYS*L6We*zfnkBDdQO>-G96NV29JL`3j?ABv*DFbot$ zA%Ma#lzN^({s*b53L=8%c><)WDwwAE)b}=sh_F~JP?n_tI-k$)LSBP{AP^vBSz@_d zP75+k6IE3SpwsCjlywqN7={9*C<+*c0Yy>Z`~Eb|(6)D9hr<Cx1lM(^Ywz~Jw#u>$ zx~@ahG^A+?V1ff{Sr+QL7EIgi7DR;IZr9D#bq&k1o`M9}!yAbv`KZzed7ejko>QLZ zbUYr%SOj6reRR28z&Y<qz&S@8$8Tr#wX2-{MLL;=0knaB^e?DijgNs^$ZR&dKhp<F bC)?LwtU||=sK@x800000NkvXXu0mjfVS?*+ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_shuffle@3x.png b/Telegram/Resources/icons/player/player_shuffle@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..14dc3efdd762e46692b73c0c7acabc36e938b5e4 GIT binary patch literal 761 zcmV<V0tWqwP)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkQt4TybR9J=0*RhVWKoAG;Arx9zm{?iTSy<Ei20Va<QZ4N5G%*po z(?V-|3Z6h(OZ5e)3<U{VSYW{T|B8zw+_wvhhkBcABeTOV3$rprMEJxUjYcAIpTPkT z0e%iA0Q@X21NeDd3h-~>m_TL}WBm5Uq9Xy!v+&ih|A2WG4F-c$*kCXy1Q!GIEbx6F zwOWk{tJP}oeZMf=cs$OB1Aygn$;7Qzt72h!A1eSvQG{l*nSeE$O+-;tEG+FPX*3!L z!;qO^v)LqKZQEwXg<*(Bqaj>4KVa$S++wj{Cb29lj?pxYy9@LAT)3(kuykCtT7~C% zi6qzS6^f#Oh)^n(;5ZHw=6N2f)v6GV153vd5&HdpB9UPj@wMS_$lbMGuP1aKroggs zvMl3pH~;`#*F~jL0TH2Iuj6vLFk$=s9<nTF;xb^_I3hx;)dB$Mx*q?wUaz^k)@rpf za|*!3a72XJYz9e^Ktxbg75Dp{2{TPoD4*aI3oMmNAR<VTgza|Agattm7t335kJ)aw zxsP))nG^}jhpSX7I3ABo*ztIbi{&G@$z;OawN9r~G)x?)C<=lgV8XWBZCosmck`_{ z(=?f|`~418RYCM!EYs;!C|_ZH>*JhbjrDpR$LYEbK<u0Y3g}y9S;l_9XTmO*3+nYc zh`x)(bzK0!ZnqQqhY$d!<9fXwch`o)VH{@|M&eqx+ZDoP0Qm`6UnP&>I1b9?GPhXr ze3hgC`MCLf&fPUl)8aVGvY0VnUthvFCXjpglwsR;A_?zH_?hu^-wN=1h}-Q}JTULV z)oR6jOhtBIF)+`9)9IAjx1P`E!f?6!)^~OMPxY-?XCki(pMr@T!mpYFu)l-L-M79A r_jKR-YTU2-)>q;R>04hhMf%o%wds9inqk}V00000NkvXXu0mjfT}ocF literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index feeece08c..846dd8a58 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -81,6 +81,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_panel.h" #include "media/player/media_player_widget.h" #include "media/player/media_player_dropdown.h" +#include "media/player/media_player_repeat_controls.h" #include "media/player/media_player_volume_controller.h" #include "media/player/media_player_instance.h" #include "media/player/media_player_float.h" @@ -813,6 +814,7 @@ void MainWidget::closeBothPlayers() { _player->hide(anim::type::normal); } _playerVolume.destroyDelayed(); + _playerRepeat.destroyDelayed(); _playerPlaylist->hideIgnoringEnterEvents(); Media::Player::instance()->stop(AudioMsgId::Type::Voice); @@ -849,6 +851,11 @@ void MainWidget::createPlayer() { _playerVolume.data(), _controller); _player->entity()->volumeWidgetCreated(_playerVolume); + _playerRepeat.create(this); + Media::Player::PrepareRepeatDropdown( + _playerRepeat.data(), + _controller); + _player->entity()->repeatWidgetCreated(_playerRepeat); orderWidgets(); if (_a_show.animating()) { _player->show(anim::type::instant); @@ -883,6 +890,7 @@ void MainWidget::playerHeightUpdated() { const auto state = Media::Player::instance()->getState(Media::Player::instance()->getActiveType()); if (!state.id || Media::Player::IsStoppedOrStopping(state.state)) { _playerVolume.destroyDelayed(); + _playerRepeat.destroyDelayed(); _player.destroyDelayed(); } } @@ -1536,6 +1544,10 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation( if (playerVolumeVisible) { _playerVolume->hide(); } + auto playerRepeatVisible = _playerRepeat && !_playerRepeat->isHidden(); + if (playerRepeatVisible) { + _playerRepeat->hide(); + } auto playerPlaylistVisible = !_playerPlaylist->isHidden(); if (playerPlaylistVisible) { _playerPlaylist->hide(); @@ -1563,6 +1575,9 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation( if (playerVolumeVisible) { _playerVolume->show(); } + if (playerRepeatVisible) { + _playerRepeat->show(); + } if (playerPlaylistVisible) { _playerPlaylist->show(); } @@ -1822,6 +1837,9 @@ void MainWidget::orderWidgets() { if (_playerVolume) { _playerVolume->raise(); } + if (_playerRepeat) { + _playerRepeat->raise(); + } _sideShadow->raise(); if (_thirdShadow) { _thirdShadow->raise(); @@ -1855,6 +1873,10 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m if (playerVolumeVisible) { _playerVolume->hide(); } + auto playerRepeatVisible = _playerRepeat && !_playerRepeat->isHidden(); + if (playerRepeatVisible) { + _playerRepeat->hide(); + } auto playerPlaylistVisible = !_playerPlaylist->isHidden(); if (playerPlaylistVisible) { _playerPlaylist->hide(); @@ -1885,6 +1907,9 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m if (playerVolumeVisible) { _playerVolume->show(); } + if (playerRepeatVisible) { + _playerRepeat->show(); + } if (playerPlaylistVisible) { _playerPlaylist->show(); } @@ -2374,11 +2399,19 @@ void MainWidget::updateThirdColumnToCurrentChat( } void MainWidget::updateMediaPlayerPosition() { - if (_player && _playerVolume) { + if (!_player) { + return; + } + if (_playerVolume) { auto relativePosition = _player->entity()->getPositionForVolumeWidget(); auto playerMargins = _playerVolume->getMargin(); _playerVolume->moveToLeft(_player->x() + relativePosition.x() - playerMargins.left(), _player->y() + relativePosition.y() - playerMargins.top()); } + if (_playerRepeat) { + auto relativePosition = _player->entity()->getPositionForRepeatWidget(); + auto playerMargins = _playerRepeat->getMargin(); + _playerRepeat->moveToLeft(_player->x() + relativePosition.x() - playerMargins.left(), _player->y() + relativePosition.y() - playerMargins.top()); + } } void MainWidget::updateMediaPlaylistPosition(int x) { @@ -2529,8 +2562,9 @@ void MainWidget::searchInChat(Dialogs::Key chat) { bool MainWidget::contentOverlapped(const QRect &globalRect) { return (_history->contentOverlapped(globalRect) - || _playerPlaylist->overlaps(globalRect) - || (_playerVolume && _playerVolume->overlaps(globalRect))); + || _playerPlaylist->overlaps(globalRect) + || (_playerVolume && _playerVolume->overlaps(globalRect))) + || (_playerRepeat && _playerRepeat->overlaps(globalRect)); } void MainWidget::activate() { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index beb97010d..91402ca22 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -368,6 +368,7 @@ private: object_ptr<Window::TopBarWrapWidget<Media::Player::Widget>> _player = { nullptr }; object_ptr<Media::Player::Dropdown> _playerVolume = { nullptr }; + object_ptr<Media::Player::Dropdown> _playerRepeat = { nullptr }; object_ptr<Media::Player::Panel> _playerPlaylist; bool _playerUsingPanel = false; diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index ab91473f1..22a726790 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -67,10 +67,40 @@ mediaPlayerRepeatDisabledIcon: icon { mediaPlayerRepeatDisabledIconOver: icon { { "player/player_repeat", menuIconFgOver, point(9px, 11px)} }; -mediaPlayerRepeatDisabledRippleBg: windowBgOver; -mediaPlayerRepeatInactiveIcon: icon { - { "player/player_repeat", mediaPlayerInactiveFg, point(9px, 11px)} +mediaPlayerRepeatOneIcon: icon { + { "player/player_repeat_one", mediaPlayerActiveFg, point(9px, 11px)} }; +mediaPlayerRepeatOneDisabledIcon: icon { + { "player/player_repeat_one", menuIconFg, point(9px, 11px)} +}; +mediaPlayerRepeatOneDisabledIconOver: icon { + { "player/player_repeat_one", menuIconFgOver, point(9px, 11px)} +}; +mediaPlayerReverseIcon: icon { + { "player/player_reverse", mediaPlayerActiveFg, point(9px, 11px)} +}; +mediaPlayerReverseDisabledIcon: icon { + { "player/player_reverse", menuIconFg, point(9px, 11px)} +}; +mediaPlayerReverseDisabledIconOver: icon { + { "player/player_reverse", menuIconFgOver, point(9px, 11px)} +}; +mediaPlayerShuffleIcon: icon { + { "player/player_shuffle", mediaPlayerActiveFg, point(9px, 11px)} +}; +mediaPlayerShuffleDisabledIcon: icon { + { "player/player_shuffle", menuIconFg, point(9px, 11px)} +}; +mediaPlayerShuffleDisabledIconOver: icon { + { "player/player_shuffle", menuIconFgOver, point(9px, 11px)} +}; +mediaPlayerRepeatReverseIcon: icon { + { "player/player_repeat_reverse", mediaPlayerActiveFg, point(9px, 11px)} +}; +mediaPlayerRepeatShuffleIcon: icon { + { "player/player_repeat_shuffle", mediaPlayerActiveFg, point(9px, 11px)} +}; +mediaPlayerRepeatDisabledRippleBg: windowBgOver; mediaPlayerSpeedButton: IconButton { width: 31px; diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp new file mode 100644 index 000000000..fe8bfc882 --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp @@ -0,0 +1,53 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "media/player/media_player_repeat_controls.h" + +#include "media/player/media_player_dropdown.h" +#include "ui/widgets/buttons.h" +#include "styles/style_media_player.h" + +namespace Media::Player { + +void PrepareRepeatDropdown( + not_null<Dropdown*> dropdown, + not_null<Window::SessionController*> controller) { + const auto makeButton = [&] { + const auto result = Ui::CreateChild<Ui::IconButton>( + dropdown.get(), + st::mediaPlayerRepeatButton); + result->show(); + return result; + }; + + const auto repeatOne = makeButton(); + const auto repeat = makeButton(); + const auto shuffle = makeButton(); + const auto reverse = makeButton(); + + repeatOne->setIconOverride(&st::mediaPlayerRepeatOneIcon); + shuffle->setIconOverride(&st::mediaPlayerShuffleIcon); + reverse->setIconOverride(&st::mediaPlayerReverseIcon); + + dropdown->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + const auto rect = QRect(QPoint(), size); + const auto inner = rect.marginsRemoved(dropdown->getMargin()); + const auto skip = (inner.height() - repeatOne->height() * 4) / 3; + auto top = 0; + const auto move = [&](auto &widget) { + widget->move((size.width() - widget->width()) / 2, top); + top += widget->height() + skip; + }; + move(repeatOne); + move(repeat); + move(shuffle); + move(reverse); + }, dropdown->lifetime()); +} + +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.h b/Telegram/SourceFiles/media/player/media_player_repeat_controls.h new file mode 100644 index 000000000..ae3f31de9 --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_repeat_controls.h @@ -0,0 +1,22 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Window { +class SessionController; +} // namespace Window + +namespace Media::Player { + +class Dropdown; + +void PrepareRepeatDropdown( + not_null<Dropdown*> dropdown, + not_null<Window::SessionController*> controller); + +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index dee206a2b..a28481de8 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -398,6 +398,17 @@ void Widget::volumeWidgetCreated(Dropdown *widget) { _volumeToggle->installEventFilter(widget); } +QPoint Widget::getPositionForRepeatWidget() const { + auto x = _repeatToggle->x(); + x += (_repeatToggle->width() - st::mediaPlayerVolumeSize.width()) / 2; + if (rtl()) x = width() - x - st::mediaPlayerVolumeSize.width(); + return QPoint(x, height()); +} + +void Widget::repeatWidgetCreated(Dropdown *widget) { + _repeatToggle->installEventFilter(widget); +} + Widget::~Widget() = default; void Widget::handleSeekProgress(float64 progress) { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 0373bec7b..e918835e9 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -53,6 +53,9 @@ public: QPoint getPositionForVolumeWidget() const; void volumeWidgetCreated(Dropdown *widget); + QPoint getPositionForRepeatWidget() const; + void repeatWidgetCreated(Dropdown *widget); + ~Widget(); protected: From 92e2b91f81caec82ef8e0f22eb179e221c7aef15 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 15:09:44 +0400 Subject: [PATCH 058/180] Add repeat / order controls to the audio player. --- Telegram/SourceFiles/core/core_settings.cpp | 27 ++++- Telegram/SourceFiles/core/core_settings.h | 32 ++++++ Telegram/SourceFiles/mainwidget.cpp | 4 +- .../media/player/media_player.style | 12 ++ .../media/player/media_player_instance.cpp | 2 +- .../media/player/media_player_instance.h | 70 ++++++++++-- .../player/media_player_repeat_controls.cpp | 76 +++++++++++-- .../player/media_player_repeat_controls.h | 8 +- .../media/player/media_player_widget.cpp | 105 ++++++++++++++++-- .../media/player/media_player_widget.h | 2 +- 10 files changed, 294 insertions(+), 44 deletions(-) diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 05eeab254..1863fb27d 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/section_widget.h" #include "base/platform/base_platform_info.h" #include "webrtc/webrtc_create_adm.h" +#include "media/player/media_player_instance.h" #include "ui/gl/gl_detection.h" #include "calls/group/calls_group_common.h" #include "facades.h" @@ -125,7 +126,8 @@ QByteArray Settings::serialize() const { + sizeof(qint32) * 2 + Serialize::bytearraySize(_photoEditorBrush) + sizeof(qint32) * 3 - + Serialize::stringSize(_customDeviceModel.current()); + + Serialize::stringSize(_customDeviceModel.current()) + + sizeof(qint32) * 2; auto result = QByteArray(); result.reserve(size); @@ -223,7 +225,9 @@ QByteArray Settings::serialize() const { << qint32(_groupCallNoiseSuppression ? 1 : 0) << qint32(_voicePlaybackSpeed * 100) << qint32(_closeToTaskbar.current() ? 1 : 0) - << _customDeviceModel.current(); + << _customDeviceModel.current() + << qint32(_playerRepeatMode.current()) + << qint32(_playerOrderMode.current()); } return result; } @@ -308,6 +312,8 @@ void Settings::addFromSerialized(const QByteArray &serialized) { QByteArray photoEditorBrush = _photoEditorBrush; qint32 closeToTaskbar = _closeToTaskbar.current() ? 1 : 0; QString customDeviceModel = _customDeviceModel.current(); + qint32 playerRepeatMode = static_cast<qint32>(_playerRepeatMode.current()); + qint32 playerOrderMode = static_cast<qint32>(_playerOrderMode.current()); stream >> themesAccentColors; if (!stream.atEnd()) { @@ -471,6 +477,11 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> customDeviceModel; } + if (!stream.atEnd()) { + stream + >> playerRepeatMode + >> playerOrderMode; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -613,6 +624,18 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _photoEditorBrush = photoEditorBrush; _closeToTaskbar = (closeToTaskbar == 1); _customDeviceModel = customDeviceModel; + const auto uncheckedPlayerRepeatMode = static_cast<Media::Player::RepeatMode>(playerRepeatMode); + switch (uncheckedPlayerRepeatMode) { + case Media::Player::RepeatMode::None: + case Media::Player::RepeatMode::One: + case Media::Player::RepeatMode::All: _playerRepeatMode = uncheckedPlayerRepeatMode; break; + } + const auto uncheckedPlayerOrderMode = static_cast<Media::Player::OrderMode>(playerOrderMode); + switch (uncheckedPlayerOrderMode) { + case Media::Player::OrderMode::Default: + case Media::Player::OrderMode::Reverse: + case Media::Player::OrderMode::Shuffle: _playerOrderMode = uncheckedPlayerOrderMode; break; + } } QString Settings::getSoundPath(const QString &key) const { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 8de40b6a3..b36112fa4 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -32,6 +32,11 @@ namespace Calls::Group { enum class StickedTooltip; } // namespace Calls::Group +namespace Media::Player { +enum class RepeatMode; +enum class OrderMode; +} // namespace Media::Player + namespace Core { struct WindowPosition { @@ -630,6 +635,31 @@ public: [[nodiscard]] rpl::producer<QString> deviceModelChanges() const; [[nodiscard]] rpl::producer<QString> deviceModelValue() const; + void setPlayerRepeatMode(Media::Player::RepeatMode mode) { + _playerRepeatMode = mode; + } + [[nodiscard]] Media::Player::RepeatMode playerRepeatMode() const { + return _playerRepeatMode.current(); + } + [[nodiscard]] rpl::producer<Media::Player::RepeatMode> playerRepeatModeValue() const { + return _playerRepeatMode.value(); + } + [[nodiscard]] rpl::producer<Media::Player::RepeatMode> playerRepeatModeChanges() const { + return _playerRepeatMode.changes(); + } + void setPlayerOrderMode(Media::Player::OrderMode mode) { + _playerOrderMode = mode; + } + [[nodiscard]] Media::Player::OrderMode playerOrderMode() const { + return _playerOrderMode.current(); + } + [[nodiscard]] rpl::producer<Media::Player::OrderMode> playerOrderModeValue() const { + return _playerOrderMode.value(); + } + [[nodiscard]] rpl::producer<Media::Player::OrderMode> playerOrderModeChanges() const { + return _playerOrderMode.changes(); + } + [[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] static float64 DefaultDialogsWidthRatio(); [[nodiscard]] static qint32 SerializePlaybackSpeed(float64 speed) { @@ -731,6 +761,8 @@ private: base::flags<Calls::Group::StickedTooltip> _hiddenGroupCallTooltips; rpl::variable<bool> _closeToTaskbar = false; rpl::variable<QString> _customDeviceModel; + rpl::variable<Media::Player::RepeatMode> _playerRepeatMode; + rpl::variable<Media::Player::OrderMode> _playerOrderMode; bool _tabbedReplacedWithInfo = false; // per-window rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 846dd8a58..f2f776d76 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -852,9 +852,7 @@ void MainWidget::createPlayer() { _controller); _player->entity()->volumeWidgetCreated(_playerVolume); _playerRepeat.create(this); - Media::Player::PrepareRepeatDropdown( - _playerRepeat.data(), - _controller); + Media::Player::PrepareRepeatDropdown(_playerRepeat.data()); _player->entity()->repeatWidgetCreated(_playerRepeat); orderWidgets(); if (_a_show.animating()) { diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 22a726790..3a3392c55 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -97,9 +97,21 @@ mediaPlayerShuffleDisabledIconOver: icon { mediaPlayerRepeatReverseIcon: icon { { "player/player_repeat_reverse", mediaPlayerActiveFg, point(9px, 11px)} }; +mediaPlayerRepeatReverseDisabledIcon: icon { + { "player/player_repeat_reverse", menuIconFg, point(9px, 11px)} +}; +mediaPlayerRepeatReverseDisabledIconOver: icon { + { "player/player_repeat_reverse", menuIconFgOver, point(9px, 11px)} +}; mediaPlayerRepeatShuffleIcon: icon { { "player/player_repeat_shuffle", mediaPlayerActiveFg, point(9px, 11px)} }; +mediaPlayerRepeatShuffleDisabledIcon: icon { + { "player/player_repeat_shuffle", menuIconFg, point(9px, 11px)} +}; +mediaPlayerRepeatShuffleDisabledIconOver: icon { + { "player/player_repeat_shuffle", menuIconFgOver, point(9px, 11px)} +}; mediaPlayerRepeatDisabledRippleBg: windowBgOver; mediaPlayerSpeedButton: IconButton { diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 7410ed7f1..099feec2e 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -746,7 +746,7 @@ void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) { } _updatedNotifier.fire_copy({state}); if (data->isPlaying && state.state == State::StoppedAtEnd) { - if (data->repeatEnabled) { + if (data->repeat.current() == RepeatMode::One) { play(data->current); } else if (!moveInPlaylist(data, 1, true)) { _tracksFinishedNotifier.notify(type); diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 47eb92898..39b9ce513 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -38,6 +38,18 @@ enum class Error; namespace Media { namespace Player { +enum class RepeatMode { + None, + One, + All, +}; + +enum class OrderMode { + Default, + Reverse, + Shuffle, +}; + class Instance; struct TrackState; @@ -104,16 +116,55 @@ public: return AudioMsgId(); } - [[nodiscard]] bool repeatEnabled(AudioMsgId::Type type) const { + [[nodiscard]] RepeatMode repeatMode(AudioMsgId::Type type) const { if (const auto data = getData(type)) { - return data->repeatEnabled; + return data->repeat.current(); } - return false; + return RepeatMode::None; } - void toggleRepeat(AudioMsgId::Type type) { + [[nodiscard]] rpl::producer<RepeatMode> repeatModeValue( + AudioMsgId::Type type) const { if (const auto data = getData(type)) { - data->repeatEnabled = !data->repeatEnabled; - _repeatChangedNotifier.notify(type); + return data->repeat.value(); + } + return rpl::single(RepeatMode::None); + } + [[nodiscard]] rpl::producer<RepeatMode> repeatModeChanges( + AudioMsgId::Type type) const { + if (const auto data = getData(type)) { + return data->repeat.changes(); + } + return rpl::never<RepeatMode>(); + } + void setRepeatMode(AudioMsgId::Type type, RepeatMode mode) { + if (const auto data = getData(type)) { + data->repeat = mode; + } + } + + [[nodiscard]] OrderMode orderMode(AudioMsgId::Type type) const { + if (const auto data = getData(type)) { + return data->order.current(); + } + return OrderMode::Default; + } + [[nodiscard]] rpl::producer<OrderMode> orderModeValue( + AudioMsgId::Type type) const { + if (const auto data = getData(type)) { + return data->order.value(); + } + return rpl::single(OrderMode::Default); + } + [[nodiscard]] rpl::producer<OrderMode> orderModeChanges( + AudioMsgId::Type type) const { + if (const auto data = getData(type)) { + return data->order.changes(); + } + return rpl::never<OrderMode>(); + } + void setOrderMode(AudioMsgId::Type type, OrderMode mode) { + if (const auto data = getData(type)) { + data->order = mode; } } @@ -149,9 +200,6 @@ public: base::Observable<AudioMsgId::Type> &trackChangedNotifier() { return _trackChangedNotifier; } - base::Observable<AudioMsgId::Type> &repeatChangedNotifier() { - return _repeatChangedNotifier; - } rpl::producer<> playlistChanges(AudioMsgId::Type type) const; @@ -192,7 +240,8 @@ private: History *history = nullptr; History *migrated = nullptr; Main::Session *session = nullptr; - bool repeatEnabled = false; + rpl::variable<RepeatMode> repeat = RepeatMode::None; + rpl::variable<OrderMode> order = OrderMode::Default; bool isPlaying = false; bool resumeOnCallEnd = false; std::unique_ptr<Streamed> streamed; @@ -278,7 +327,6 @@ private: base::Observable<bool> _playerWidgetOver; base::Observable<AudioMsgId::Type> _tracksFinishedNotifier; base::Observable<AudioMsgId::Type> _trackChangedNotifier; - base::Observable<AudioMsgId::Type> _repeatChangedNotifier; rpl::event_stream<AudioMsgId::Type> _playerStopped; rpl::event_stream<AudioMsgId::Type> _playerStartedPlay; diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp index fe8bfc882..302852dd0 100644 --- a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp +++ b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp @@ -8,14 +8,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_repeat_controls.h" #include "media/player/media_player_dropdown.h" +#include "media/player/media_player_instance.h" #include "ui/widgets/buttons.h" +#include "core/core_settings.h" +#include "core/application.h" #include "styles/style_media_player.h" namespace Media::Player { -void PrepareRepeatDropdown( - not_null<Dropdown*> dropdown, - not_null<Window::SessionController*> controller) { +void PrepareRepeatDropdown(not_null<Dropdown*> dropdown) { const auto makeButton = [&] { const auto result = Ui::CreateChild<Ui::IconButton>( dropdown.get(), @@ -25,13 +26,72 @@ void PrepareRepeatDropdown( }; const auto repeatOne = makeButton(); - const auto repeat = makeButton(); + const auto repeatAll = makeButton(); const auto shuffle = makeButton(); const auto reverse = makeButton(); - repeatOne->setIconOverride(&st::mediaPlayerRepeatOneIcon); - shuffle->setIconOverride(&st::mediaPlayerShuffleIcon); - reverse->setIconOverride(&st::mediaPlayerReverseIcon); + Core::App().settings().playerRepeatModeValue( + ) | rpl::start_with_next([=](RepeatMode mode) { + const auto one = (mode == RepeatMode::One); + repeatOne->setIconOverride(one + ? &st::mediaPlayerRepeatOneIcon + : &st::mediaPlayerRepeatOneDisabledIcon, + one ? nullptr : &st::mediaPlayerRepeatOneDisabledIconOver); + repeatOne->setRippleColorOverride( + one ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); + const auto all = (mode == RepeatMode::All); + repeatAll->setIconOverride(all + ? nullptr + : &st::mediaPlayerRepeatDisabledIcon, + all ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); + repeatAll->setRippleColorOverride( + all ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); + }, dropdown->lifetime()); + + Core::App().settings().playerOrderModeValue( + ) | rpl::start_with_next([=](OrderMode mode) { + const auto shuffled = (mode == OrderMode::Shuffle); + shuffle->setIconOverride(shuffled + ? &st::mediaPlayerShuffleIcon + : &st::mediaPlayerShuffleDisabledIcon, + shuffled ? nullptr : &st::mediaPlayerShuffleDisabledIconOver); + shuffle->setRippleColorOverride( + shuffled ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); + const auto reversed = (mode == OrderMode::Reverse); + reverse->setIconOverride(reversed + ? &st::mediaPlayerReverseIcon + : &st::mediaPlayerReverseDisabledIcon, + reversed ? nullptr : &st::mediaPlayerReverseDisabledIconOver); + reverse->setRippleColorOverride( + reversed ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); + }, dropdown->lifetime()); + + const auto toggleRepeat = [](RepeatMode mode) { + auto &settings = Core::App().settings(); + const auto active = (settings.playerRepeatMode() == mode); + settings.setPlayerRepeatMode(active ? RepeatMode::None : mode); + const auto type = AudioMsgId::Type::Song; + instance()->setRepeatMode(type, settings.playerRepeatMode()); + if (!active) { + instance()->setOrderMode(type, settings.playerOrderMode()); + } + Core::App().saveSettingsDelayed(); + }; + const auto toggleOrder = [](OrderMode mode) { + auto &settings = Core::App().settings(); + const auto active = (settings.playerOrderMode() == mode); + settings.setPlayerOrderMode(active ? OrderMode::Default : mode); + const auto type = AudioMsgId::Type::Song; + instance()->setOrderMode(type, settings.playerOrderMode()); + if (!active) { + instance()->setRepeatMode(type, settings.playerRepeatMode()); + } + Core::App().saveSettingsDelayed(); + }; + repeatOne->setClickedCallback([=] { toggleRepeat(RepeatMode::One); }); + repeatAll->setClickedCallback([=] { toggleRepeat(RepeatMode::All); }); + shuffle->setClickedCallback([=] { toggleOrder(OrderMode::Shuffle); }); + reverse->setClickedCallback([=] { toggleOrder(OrderMode::Reverse); }); dropdown->sizeValue( ) | rpl::start_with_next([=](QSize size) { @@ -44,7 +104,7 @@ void PrepareRepeatDropdown( top += widget->height() + skip; }; move(repeatOne); - move(repeat); + move(repeatAll); move(shuffle); move(reverse); }, dropdown->lifetime()); diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.h b/Telegram/SourceFiles/media/player/media_player_repeat_controls.h index ae3f31de9..17cac6868 100644 --- a/Telegram/SourceFiles/media/player/media_player_repeat_controls.h +++ b/Telegram/SourceFiles/media/player/media_player_repeat_controls.h @@ -7,16 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -namespace Window { -class SessionController; -} // namespace Window - namespace Media::Player { class Dropdown; -void PrepareRepeatDropdown( - not_null<Dropdown*> dropdown, - not_null<Window::SessionController*> controller); +void PrepareRepeatDropdown(not_null<Dropdown*> dropdown); } // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index a28481de8..5083f83a6 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -290,9 +290,33 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) updateVolumeToggleIcon(); }, lifetime()); - updateRepeatTrackIcon(); + rpl::combine( + Core::App().settings().playerRepeatModeValue(), + Core::App().settings().playerOrderModeValue(), + instance()->repeatModeValue(AudioMsgId::Type::Song), + instance()->orderModeValue(AudioMsgId::Type::Song) + ) | rpl::start_with_next([=] { + updateRepeatToggleIcon(); + }, lifetime()); _repeatToggle->setClickedCallback([=] { - instance()->toggleRepeat(AudioMsgId::Type::Song); + const auto type = AudioMsgId::Type::Song; + const auto repeat = Core::App().settings().playerRepeatMode(); + const auto order = Core::App().settings().playerOrderMode(); + const auto mayBeActive = (repeat != RepeatMode::None) + || (order != OrderMode::Default); + const auto active = mayBeActive + && (repeat == instance()->repeatMode(type)) + && (order == instance()->orderMode(type)); + if (!active && !mayBeActive) { + Core::App().settings().setPlayerRepeatMode(RepeatMode::All); + Core::App().saveSettingsDelayed(); + } + instance()->setRepeatMode(type, active + ? RepeatMode::None + : mayBeActive + ? repeat + : RepeatMode::All); + instance()->setOrderMode(type, active ? OrderMode::Default : order); }); _playbackSpeed->saved( @@ -300,11 +324,6 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) instance()->updateVoicePlaybackSpeed(); }, lifetime()); - subscribe(instance()->repeatChangedNotifier(), [this](AudioMsgId::Type type) { - if (type == _type) { - updateRepeatTrackIcon(); - } - }); subscribe(instance()->trackChangedNotifier(), [this](AudioMsgId::Type type) { if (type == _type) { handleSongChange(); @@ -552,10 +571,74 @@ void Widget::updateLabelsGeometry() { _timeLabel->moveToRight(right, st::mediaPlayerNameTop - st::mediaPlayerTime.font->ascent); } -void Widget::updateRepeatTrackIcon() { - auto repeating = instance()->repeatEnabled(AudioMsgId::Type::Song); - _repeatToggle->setIconOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledIcon, repeating ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); - _repeatToggle->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); +void Widget::updateRepeatToggleIcon() { + const auto type = AudioMsgId::Type::Song; + const auto repeat = Core::App().settings().playerRepeatMode(); + const auto order = Core::App().settings().playerOrderMode(); + const auto active = (repeat == instance()->repeatMode(type)) + && (order == instance()->orderMode(type)) + && (repeat != RepeatMode::None || order != OrderMode::Default); + switch (repeat) { + case RepeatMode::None: + switch (order) { + case OrderMode::Default: + _repeatToggle->setIconOverride( + &st::mediaPlayerRepeatDisabledIcon, + &st::mediaPlayerRepeatDisabledIconOver); + break; + case OrderMode::Reverse: + _repeatToggle->setIconOverride( + (active + ? &st::mediaPlayerReverseIcon + : &st::mediaPlayerReverseDisabledIcon), + active ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); + break; + case OrderMode::Shuffle: + _repeatToggle->setIconOverride( + (active + ? &st::mediaPlayerShuffleIcon + : &st::mediaPlayerShuffleDisabledIcon), + active ? nullptr : &st::mediaPlayerShuffleDisabledIconOver); + break; + } + break; + case RepeatMode::One: + _repeatToggle->setIconOverride( + (active + ? &st::mediaPlayerRepeatOneIcon + : &st::mediaPlayerRepeatOneDisabledIcon), + active ? nullptr : &st::mediaPlayerRepeatOneDisabledIconOver); + break; + case RepeatMode::All: + switch (order) { + case OrderMode::Default: + _repeatToggle->setIconOverride( + (active ? nullptr : &st::mediaPlayerRepeatDisabledIcon), + (active ? nullptr : &st::mediaPlayerRepeatDisabledIconOver)); + break; + case OrderMode::Reverse: + _repeatToggle->setIconOverride( + (active + ? &st::mediaPlayerRepeatReverseIcon + : &st::mediaPlayerRepeatReverseDisabledIcon), + (active + ? nullptr + : &st::mediaPlayerRepeatReverseDisabledIconOver)); + break; + case OrderMode::Shuffle: + _repeatToggle->setIconOverride( + (active + ? &st::mediaPlayerRepeatShuffleIcon + : &st::mediaPlayerRepeatShuffleDisabledIcon), + (active + ? nullptr + : &st::mediaPlayerRepeatShuffleDisabledIconOver)); + break; + } + break; + } + _repeatToggle->setRippleColorOverride( + active ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); } void Widget::checkForTypeChange() { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index e918835e9..7b8f91301 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -78,7 +78,7 @@ private: void updatePlayPrevNextPositions(); void updateLabelsGeometry(); - void updateRepeatTrackIcon(); + void updateRepeatToggleIcon(); void updateControlsVisibility(); void updateControlsGeometry(); void createPrevNextButtons(); From 74cfaff100af5273288526d5782290feb4a8de09 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 15:55:09 +0400 Subject: [PATCH 059/180] Migrate Media::Player::Instance to rpl. --- .../SourceFiles/history/history_widget.cpp | 12 +++--- Telegram/SourceFiles/mainwidget.cpp | 33 ++++++++-------- .../media/player/media_player_float.cpp | 12 +++--- .../media/player/media_player_float.h | 2 + .../media/player/media_player_instance.cpp | 12 ++++-- .../media/player/media_player_instance.h | 37 +++++++++--------- .../media/player/media_player_widget.cpp | 38 ++++++++++--------- .../media/player/media_player_widget.h | 9 ++++- .../media/system_media_controls_manager.cpp | 3 +- 9 files changed, 86 insertions(+), 72 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 17cffd96e..450fea245 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -634,11 +634,13 @@ HistoryWidget::HistoryWidget( } }, lifetime()); - subscribe(Media::Player::instance()->switchToNextNotifier(), [this](const Media::Player::Instance::Switch &pair) { - if (pair.from.type() == AudioMsgId::Type::Voice) { - scrollToCurrentVoiceMessage(pair.from.contextId(), pair.to); - } - }); + using MediaSwitch = Media::Player::Instance::Switch; + Media::Player::instance()->switchToNextEvents( + ) | rpl::filter([=](const MediaSwitch &pair) { + return (pair.from.type() == AudioMsgId::Type::Voice); + }) | rpl::start_with_next([=](const MediaSwitch &pair) { + scrollToCurrentVoiceMessage(pair.from.contextId(), pair.to); + }, lifetime()); using PeerUpdateFlag = Data::PeerUpdate::Flag; session().changes().peerUpdates( diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index f2f776d76..6d1109ac0 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -333,20 +333,8 @@ MainWidget::MainWidget( QCoreApplication::instance()->installEventFilter(this); - subscribe(Media::Player::instance()->playerWidgetOver(), [this](bool over) { - if (over) { - if (_playerPlaylist->isHidden()) { - auto position = mapFromGlobal(QCursor::pos()).x(); - auto bestPosition = _playerPlaylist->bestPositionFor(position); - if (rtl()) bestPosition = position + 2 * (position - bestPosition) - _playerPlaylist->width(); - updateMediaPlaylistPosition(bestPosition); - } - _playerPlaylist->showFromOther(); - } else { - _playerPlaylist->hideFromOther(); - } - }); - subscribe(Media::Player::instance()->tracksFinishedNotifier(), [this](AudioMsgId::Type type) { + Media::Player::instance()->tracksFinished( + ) | rpl::start_with_next([=](AudioMsgId::Type type) { if (type == AudioMsgId::Type::Voice) { const auto songState = Media::Player::instance()->getState(AudioMsgId::Type::Song); if (!songState.id || IsStoppedOrStopping(songState.state)) { @@ -358,7 +346,7 @@ MainWidget::MainWidget( closeBothPlayers(); } } - }); + }, lifetime()); _controller->adaptive().changes( ) | rpl::start_with_next([=] { @@ -846,6 +834,21 @@ void MainWidget::createPlayer() { not_null<const HistoryItem*> item) { _controller->showPeerHistoryAtItem(item); }); + + _player->entity()->togglePlaylistRequests( + ) | rpl::start_with_next([=](bool shown) { + if (!shown) { + _playerPlaylist->hideFromOther(); + return; + } else if (_playerPlaylist->isHidden()) { + auto position = mapFromGlobal(QCursor::pos()).x(); + auto bestPosition = _playerPlaylist->bestPositionFor(position); + if (rtl()) bestPosition = position + 2 * (position - bestPosition) - _playerPlaylist->width(); + updateMediaPlaylistPosition(bestPosition); + } + _playerPlaylist->showFromOther(); + }, _player->lifetime()); + _playerVolume.create(this); Media::Player::PrepareVolumeDropdown( _playerVolume.data(), diff --git a/Telegram/SourceFiles/media/player/media_player_float.cpp b/Telegram/SourceFiles/media/player/media_player_float.cpp index bcd25405b..e1899950f 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.cpp +++ b/Telegram/SourceFiles/media/player/media_player_float.cpp @@ -299,12 +299,12 @@ FloatController::Item::Item( FloatController::FloatController(not_null<FloatDelegate*> delegate) : _delegate(delegate) , _parent(_delegate->floatPlayerWidget()) { - subscribe(Media::Player::instance()->trackChangedNotifier(), [=]( - AudioMsgId::Type type) { - if (type == AudioMsgId::Type::Voice) { - checkCurrent(); - } - }); + Media::Player::instance()->trackChanged( + ) | rpl::filter([=](AudioMsgId::Type type) { + return (type == AudioMsgId::Type::Voice); + }) | rpl::start_with_next([=] { + checkCurrent(); + }, _lifetime); startDelegateHandling(); } diff --git a/Telegram/SourceFiles/media/player/media_player_float.h b/Telegram/SourceFiles/media/player/media_player_float.h index 9edb4af41..6c3b0109e 100644 --- a/Telegram/SourceFiles/media/player/media_player_float.h +++ b/Telegram/SourceFiles/media/player/media_player_float.h @@ -262,6 +262,8 @@ private: rpl::event_stream<FullMsgId> _closeEvents; rpl::lifetime _delegateLifetime; + rpl::lifetime _lifetime; + }; } // namespace Player diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 099feec2e..617d4b189 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -182,7 +182,7 @@ void Instance::setCurrent(const AudioMsgId &audioId) { data->migrated = nullptr; data->session = nullptr; } - _trackChangedNotifier.notify(data->type, true); + _trackChanged.fire_copy(data->type); refreshPlaylist(data); } } @@ -369,7 +369,7 @@ bool Instance::moveInPlaylist( if (const auto media = item->media()) { if (const auto document = media->document()) { if (autonext) { - _switchToNextNotifier.notify({ + _switchToNextStream.fire({ data->current, item->fullId() }); @@ -558,8 +558,8 @@ void Instance::stop(AudioMsgId::Type type) { void Instance::stopAndClear(not_null<Data*> data) { stop(data->type); - _tracksFinishedNotifier.notify(data->type); *data = Data(data->type, data->overview); + _tracksFinished.fire_copy(data->type); } void Instance::playPause(AudioMsgId::Type type) { @@ -744,15 +744,19 @@ void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) { streamed->progress.updateState(state); } } + auto finished = false; _updatedNotifier.fire_copy({state}); if (data->isPlaying && state.state == State::StoppedAtEnd) { if (data->repeat.current() == RepeatMode::One) { play(data->current); } else if (!moveInPlaylist(data, 1, true)) { - _tracksFinishedNotifier.notify(type); + finished = true; } } data->isPlaying = !IsStopped(state.state); + if (finished) { + _tracksFinished.fire_copy(type); + } } } diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 39b9ce513..9645c3ec5 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -188,31 +188,30 @@ public: FullMsgId to; }; - base::Observable<Switch> &switchToNextNotifier() { - return _switchToNextNotifier; + [[nodiscard]] rpl::producer<Switch> switchToNextEvents() const { + return _switchToNextStream.events(); } - base::Observable<bool> &playerWidgetOver() { - return _playerWidgetOver; + [[nodiscard]] rpl::producer<AudioMsgId::Type> tracksFinished() const { + return _tracksFinished.events(); } - base::Observable<AudioMsgId::Type> &tracksFinishedNotifier() { - return _tracksFinishedNotifier; - } - base::Observable<AudioMsgId::Type> &trackChangedNotifier() { - return _trackChangedNotifier; + [[nodiscard]] rpl::producer<AudioMsgId::Type> trackChanged() const { + return _trackChanged.events(); } - rpl::producer<> playlistChanges(AudioMsgId::Type type) const; + [[nodiscard]] rpl::producer<> playlistChanges( + AudioMsgId::Type type) const; - rpl::producer<TrackState> updatedNotifier() const { + [[nodiscard]] rpl::producer<TrackState> updatedNotifier() const { return _updatedNotifier.events(); } - rpl::producer<> stops(AudioMsgId::Type type) const; - rpl::producer<> startsPlay(AudioMsgId::Type type) const; + [[nodiscard]] rpl::producer<> stops(AudioMsgId::Type type) const; + [[nodiscard]] rpl::producer<> startsPlay(AudioMsgId::Type type) const; - rpl::producer<Seeking> seekingChanges(AudioMsgId::Type type) const; + [[nodiscard]] rpl::producer<Seeking> seekingChanges( + AudioMsgId::Type type) const; - bool pauseGifByRoundVideo() const; + [[nodiscard]] bool pauseGifByRoundVideo() const; void documentLoadProgress(DocumentData *document); @@ -323,11 +322,9 @@ private: Data _voiceData; bool _roundPlaying = false; - base::Observable<Switch> _switchToNextNotifier; - base::Observable<bool> _playerWidgetOver; - base::Observable<AudioMsgId::Type> _tracksFinishedNotifier; - base::Observable<AudioMsgId::Type> _trackChangedNotifier; - + rpl::event_stream<Switch> _switchToNextStream; + rpl::event_stream<AudioMsgId::Type> _tracksFinished; + rpl::event_stream<AudioMsgId::Type> _trackChanged; rpl::event_stream<AudioMsgId::Type> _playerStopped; rpl::event_stream<AudioMsgId::Type> _playerStartedPlay; rpl::event_stream<TrackState> _updatedNotifier; diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 5083f83a6..c0c5836bc 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -324,23 +324,26 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) instance()->updateVoicePlaybackSpeed(); }, lifetime()); - subscribe(instance()->trackChangedNotifier(), [this](AudioMsgId::Type type) { - if (type == _type) { - handleSongChange(); - updateControlsVisibility(); - updateLabelsGeometry(); + instance()->trackChanged( + ) | rpl::filter([=](AudioMsgId::Type type) { + return (type == _type); + }) | rpl::start_with_next([=](AudioMsgId::Type type) { + handleSongChange(); + updateControlsVisibility(); + updateLabelsGeometry(); + }, lifetime()); + + instance()->tracksFinished( + ) | rpl::filter([=](AudioMsgId::Type type) { + return (type == AudioMsgId::Type::Voice); + }) | rpl::start_with_next([=](AudioMsgId::Type type) { + _voiceIsActive = false; + const auto currentSong = instance()->current(AudioMsgId::Type::Song); + const auto songState = instance()->getState(AudioMsgId::Type::Song); + if (currentSong == songState.id && !IsStoppedOrStopping(songState.state)) { + setType(AudioMsgId::Type::Song); } - }); - subscribe(instance()->tracksFinishedNotifier(), [this](AudioMsgId::Type type) { - if (type == AudioMsgId::Type::Voice) { - _voiceIsActive = false; - const auto currentSong = instance()->current(AudioMsgId::Type::Song); - const auto songState = instance()->getState(AudioMsgId::Type::Song); - if (currentSong == songState.id && !IsStoppedOrStopping(songState.state)) { - setType(AudioMsgId::Type::Song); - } - } - }); + }, lifetime()); instance()->updatedNotifier( ) | rpl::start_with_next([=](const TrackState &state) { @@ -521,8 +524,7 @@ void Widget::updateOverLabelsState(bool over) { _labelsOver = over; auto pressShowsItem = _labelsOver && (_type == AudioMsgId::Type::Voice); setCursor(pressShowsItem ? style::cur_pointer : style::cur_default); - auto showPlaylist = over && (_type == AudioMsgId::Type::Song); - instance()->playerWidgetOver().notify(showPlaylist, true); + _togglePlaylistRequests.fire(over && (_type == AudioMsgId::Type::Song)); } void Widget::updatePlayPrevNextPositions() { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 7b8f91301..aeb8131f9 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -50,12 +50,16 @@ public: void showShadow(); void hideShadow(); - QPoint getPositionForVolumeWidget() const; + [[nodiscard]] QPoint getPositionForVolumeWidget() const; void volumeWidgetCreated(Dropdown *widget); - QPoint getPositionForRepeatWidget() const; + [[nodiscard]] QPoint getPositionForRepeatWidget() const; void repeatWidgetCreated(Dropdown *widget); + [[nodiscard]] rpl::producer<bool> togglePlaylistRequests() const { + return _togglePlaylistRequests.events(); + } + ~Widget(); protected: @@ -114,6 +118,7 @@ private: bool _labelsOver = false; bool _labelsDown = false; + rpl::event_stream<bool> _togglePlaylistRequests; class PlayButton; class SpeedButton; diff --git a/Telegram/SourceFiles/media/system_media_controls_manager.cpp b/Telegram/SourceFiles/media/system_media_controls_manager.cpp index 43a25de58..5e37f2f04 100644 --- a/Telegram/SourceFiles/media/system_media_controls_manager.cpp +++ b/Telegram/SourceFiles/media/system_media_controls_manager.cpp @@ -96,8 +96,7 @@ SystemMediaControlsManager::SystemMediaControlsManager( _lifetimeDownload.destroy(); }, _lifetime); - auto trackChanged = base::ObservableViewer( - mediaPlayer->trackChangedNotifier() + auto trackChanged = mediaPlayer->trackChanged( ) | rpl::filter([=](AudioMsgId::Type audioType) { return audioType == type; }); From 4f02f722ae3cc08b9bd8929bd6ecfe5740cc0f3c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 16:42:14 +0400 Subject: [PATCH 060/180] Fix order of playlist and volume / settings dropdowns. --- Telegram/SourceFiles/mainwidget.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 6d1109ac0..5a4f77046 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1835,12 +1835,6 @@ void MainWidget::orderWidgets() { if (_callTopBar) { _callTopBar->raise(); } - if (_playerVolume) { - _playerVolume->raise(); - } - if (_playerRepeat) { - _playerRepeat->raise(); - } _sideShadow->raise(); if (_thirdShadow) { _thirdShadow->raise(); @@ -1852,8 +1846,14 @@ void MainWidget::orderWidgets() { _thirdColumnResizeArea->raise(); } _connecting->raise(); - _playerPlaylist->raise(); floatPlayerRaiseAll(); + _playerPlaylist->raise(); + if (_playerVolume) { + _playerVolume->raise(); + } + if (_playerRepeat) { + _playerRepeat->raise(); + } if (_hider) _hider->raise(); } From 68378cf8e5b79ede04f77857cc7ab322302c0db5 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 16:42:28 +0400 Subject: [PATCH 061/180] Support reversed order in the playlist. --- .../media/player/media_player_instance.cpp | 30 +++++++++++++------ .../media/player/media_player_instance.h | 4 +-- .../media/player/media_player_widget.cpp | 4 +-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 617d4b189..7bf1f1c39 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -364,12 +364,13 @@ bool Instance::moveInPlaylist( if (!data->playlistIndex) { return false; } - const auto newIndex = *data->playlistIndex + delta; + const auto newIndex = *data->playlistIndex + + (data->order.current() == OrderMode::Reverse ? -delta : delta); if (const auto item = itemByIndex(data, newIndex)) { if (const auto media = item->media()) { if (const auto document = media->document()) { if (autonext) { - _switchToNextStream.fire({ + _switchToNext.fire({ data->current, item->fullId() }); @@ -389,24 +390,35 @@ bool Instance::moveInPlaylist( bool Instance::previousAvailable(AudioMsgId::Type type) const { const auto data = getData(type); Assert(data != nullptr); - return data->playlistIndex - && data->playlistSlice - && (*data->playlistIndex > 0); + + if (!data->playlistIndex || !data->playlistSlice) { + return false; + } + return (data->order.current() == OrderMode::Reverse) + ? (*data->playlistIndex + 1 < data->playlistSlice->size()) + : (*data->playlistIndex > 0); } bool Instance::nextAvailable(AudioMsgId::Type type) const { const auto data = getData(type); Assert(data != nullptr); - return data->playlistIndex - && data->playlistSlice - && (*data->playlistIndex + 1 < data->playlistSlice->size()); + + if (!data->playlistIndex || !data->playlistSlice) { + return false; + } + return (data->order.current() == OrderMode::Reverse) + ? (*data->playlistIndex > 0) + : (*data->playlistIndex + 1 < data->playlistSlice->size()); } rpl::producer<> Media::Player::Instance::playlistChanges( AudioMsgId::Type type) const { const auto data = getData(type); Assert(data != nullptr); - return data->playlistChanges.events(); + + return rpl::merge( + data->playlistChanges.events(), + data->order.changes() | rpl::to_empty); } rpl::producer<> Media::Player::Instance::stops(AudioMsgId::Type type) const { diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 9645c3ec5..185d80765 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -189,7 +189,7 @@ public: }; [[nodiscard]] rpl::producer<Switch> switchToNextEvents() const { - return _switchToNextStream.events(); + return _switchToNext.events(); } [[nodiscard]] rpl::producer<AudioMsgId::Type> tracksFinished() const { return _tracksFinished.events(); @@ -322,7 +322,7 @@ private: Data _voiceData; bool _roundPlaying = false; - rpl::event_stream<Switch> _switchToNextStream; + rpl::event_stream<Switch> _switchToNext; rpl::event_stream<AudioMsgId::Type> _tracksFinished; rpl::event_stream<AudioMsgId::Type> _trackChanged; rpl::event_stream<AudioMsgId::Type> _playerStopped; diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index c0c5836bc..96f513df4 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -826,10 +826,10 @@ void Widget::handlePlaylistUpdate() { createPrevNextButtons(); _previousTrack->setIconOverride(previousEnabled ? nullptr : &st::mediaPlayerPreviousDisabledIcon); _previousTrack->setRippleColorOverride(previousEnabled ? nullptr : &st::mediaPlayerBg); - _previousTrack->setCursor(previousEnabled ? style::cur_pointer : style::cur_default); + _previousTrack->setPointerCursor(previousEnabled); _nextTrack->setIconOverride(nextEnabled ? nullptr : &st::mediaPlayerNextDisabledIcon); _nextTrack->setRippleColorOverride(nextEnabled ? nullptr : &st::mediaPlayerBg); - _nextTrack->setCursor(nextEnabled ? style::cur_pointer : style::cur_default); + _nextTrack->setPointerCursor(nextEnabled); } } From ecb4d1d9badf46d0cb94bc900233617816145562 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 17:52:07 +0400 Subject: [PATCH 062/180] Support repeat all in the audio player. --- .../media/player/media_player_instance.cpp | 118 +++++++++++++++++- .../media/player/media_player_instance.h | 23 ++-- .../media/player/media_player_widget.cpp | 2 +- 3 files changed, 124 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 7bf1f1c39..f32825002 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -104,6 +104,10 @@ Instance::Streamed::Streamed( Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview) : type(type) , overview(overview) { + if (type == AudioMsgId::Type::Song) { + repeat = Core::App().settings().playerRepeatModeValue(); + order = Core::App().settings().playerOrderModeValue(); + } } Instance::Data::Data(Data &&other) = default; @@ -203,6 +207,7 @@ void Instance::setSession(not_null<Data*> data, Main::Session *session) { return; } data->playlistLifetime.destroy(); + data->playlistOtherLifetime.destroy(); data->sessionLifetime.destroy(); data->session = session; if (session) { @@ -249,6 +254,8 @@ void Instance::clearStreamed(not_null<Data*> data, bool savePosition) { void Instance::refreshPlaylist(not_null<Data*> data) { if (!validPlaylist(data)) { validatePlaylist(data); + } else if (!validOtherPlaylist(data)) { + validateOtherPlaylist(data); } playlistUpdated(data); } @@ -263,7 +270,7 @@ void Instance::playlistUpdated(not_null<Data*> data) { data->playlistChanges.fire({}); } -bool Instance::validPlaylist(not_null<Data*> data) { +bool Instance::validPlaylist(not_null<const Data*> data) const { if (const auto key = playlistKey(data)) { if (!data->playlistSlice) { return false; @@ -324,7 +331,7 @@ void Instance::validatePlaylist(not_null<Data*> data) { } } -auto Instance::playlistKey(not_null<Data*> data) const +auto Instance::playlistKey(not_null<const Data*> data) const -> std::optional<SliceKey> { const auto contextId = data->current.contextId(); const auto history = data->history; @@ -346,6 +353,67 @@ auto Instance::playlistKey(not_null<Data*> data) const item->isScheduled()); } +bool Instance::validOtherPlaylist(not_null<const Data*> data) const { + if (const auto key = playlistOtherKey(data)) { + return data->playlistOtherSlice + && (key == data->playlistOtherRequestedKey); + } + return !data->playlistOtherSlice; +} + +void Instance::validateOtherPlaylist(not_null<Data*> data) { + data->playlistOtherLifetime.destroy(); + if (const auto key = playlistOtherKey(data)) { + data->playlistOtherRequestedKey = key; + + SharedMediaMergedViewer( + &data->history->session(), + SharedMediaMergedKey(*key, data->overview), + kIdsLimit, + kIdsLimit + ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) { + data->playlistOtherSlice = std::move(update); + playlistUpdated(data); + }, data->playlistOtherLifetime); + } else { + data->playlistOtherSlice = std::nullopt; + data->playlistOtherRequestedKey = std::nullopt; + playlistUpdated(data); + } +} + +auto Instance::playlistOtherKey(not_null<const Data*> data) const +-> std::optional<SliceKey> { + if (data->repeat.current() != RepeatMode::All + || data->order.current() == OrderMode::Shuffle + || !data->playlistSlice + || (data->playlistSlice->skippedBefore() != 0 + && data->playlistSlice->skippedAfter() != 0) + || (data->playlistSlice->skippedBefore() == 0 + && data->playlistSlice->skippedAfter() == 0)) { + return {}; + } + const auto contextId = data->current.contextId(); + const auto history = data->history; + if (!contextId || !history) { + return {}; + } + const auto item = data->history->owner().message(contextId); + if (!item || !item->isRegular()) { + return {}; + } + + return SliceKey( + data->history->peer->id, + data->migrated ? data->migrated->peer->id : 0, + (data->playlistSlice->skippedBefore() == 0 + ? ServerMaxMsgId - 1 + : data->migrated + ? (1 - ServerMaxMsgId) + : 1), + false); +} + HistoryItem *Instance::itemByIndex(not_null<Data*> data, int index) { if (!data->playlistSlice || index < 0 @@ -364,9 +432,7 @@ bool Instance::moveInPlaylist( if (!data->playlistIndex) { return false; } - const auto newIndex = *data->playlistIndex - + (data->order.current() == OrderMode::Reverse ? -delta : delta); - if (const auto item = itemByIndex(data, newIndex)) { + const auto jumpByItem = [&](not_null<HistoryItem*> item) { if (const auto media = item->media()) { if (const auto document = media->document()) { if (autonext) { @@ -383,6 +449,24 @@ bool Instance::moveInPlaylist( return true; } } + return false; + }; + const auto jumpById = [&](FullMsgId id) { + return jumpByItem(data->history->owner().message(id)); + }; + const auto newIndex = *data->playlistIndex + + (data->order.current() == OrderMode::Reverse ? -delta : delta); + if (const auto item = itemByIndex(data, newIndex)) { + return jumpByItem(item); + } else if (data->repeat.current() == RepeatMode::All + && data->playlistOtherSlice + && data->playlistOtherSlice->size() > 0) { + const auto &other = *data->playlistOtherSlice; + if (newIndex < 0 && other.skippedAfter() == 0) { + return jumpById(other[other.size() - 1]); + } else if (newIndex > 0 && other.skippedBefore() == 0) { + return jumpById(other[0]); + } } return false; } @@ -393,6 +477,8 @@ bool Instance::previousAvailable(AudioMsgId::Type type) const { if (!data->playlistIndex || !data->playlistSlice) { return false; + } else if (data->repeat.current() == RepeatMode::All) { + return true; } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex + 1 < data->playlistSlice->size()) @@ -405,6 +491,8 @@ bool Instance::nextAvailable(AudioMsgId::Type type) const { if (!data->playlistIndex || !data->playlistSlice) { return false; + } else if (data->repeat.current() == RepeatMode::All) { + return true; } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex > 0) @@ -418,7 +506,8 @@ rpl::producer<> Media::Player::Instance::playlistChanges( return rpl::merge( data->playlistChanges.events(), - data->order.changes() | rpl::to_empty); + data->order.changes() | rpl::to_empty, + data->repeat.changes() | rpl::to_empty); } rpl::producer<> Media::Player::Instance::stops(AudioMsgId::Type type) const { @@ -651,6 +740,23 @@ void Instance::playPauseCancelClicked(AudioMsgId::Type type) { } } +void Instance::setRepeatMode(AudioMsgId::Type type, RepeatMode mode) { + if (const auto data = getData(type)) { + const auto otherNeeded = (mode == RepeatMode::All) + && (data->repeat.current() != RepeatMode::All); + data->repeat = mode; + if (otherNeeded) { + refreshPlaylist(data); + } + } +} + +void Instance::setOrderMode(AudioMsgId::Type type, OrderMode mode) { + if (const auto data = getData(type)) { + data->order = mode; + } +} + void Instance::startSeeking(AudioMsgId::Type type) { if (auto data = getData(type)) { data->seeking = data->current; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 185d80765..d5f129bbd 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -136,11 +136,7 @@ public: } return rpl::never<RepeatMode>(); } - void setRepeatMode(AudioMsgId::Type type, RepeatMode mode) { - if (const auto data = getData(type)) { - data->repeat = mode; - } - } + void setRepeatMode(AudioMsgId::Type type, RepeatMode mode); [[nodiscard]] OrderMode orderMode(AudioMsgId::Type type) const { if (const auto data = getData(type)) { @@ -162,11 +158,7 @@ public: } return rpl::never<OrderMode>(); } - void setOrderMode(AudioMsgId::Type type, OrderMode mode) { - if (const auto data = getData(type)) { - data->order = mode; - } - } + void setOrderMode(AudioMsgId::Type type, OrderMode mode); [[nodiscard]] bool isSeeking(AudioMsgId::Type type) const { if (const auto data = getData(type)) { @@ -232,8 +224,11 @@ private: std::optional<SparseIdsMergedSlice> playlistSlice; std::optional<SliceKey> playlistSliceKey; std::optional<SliceKey> playlistRequestedKey; + std::optional<SparseIdsMergedSlice> playlistOtherSlice; + std::optional<SliceKey> playlistOtherRequestedKey; std::optional<int> playlistIndex; rpl::lifetime playlistLifetime; + rpl::lifetime playlistOtherLifetime; rpl::lifetime sessionLifetime; rpl::event_stream<> playlistChanges; History *history = nullptr; @@ -273,9 +268,13 @@ private: void setCurrent(const AudioMsgId &audioId); void refreshPlaylist(not_null<Data*> data); - std::optional<SliceKey> playlistKey(not_null<Data*> data) const; - bool validPlaylist(not_null<Data*> data); + std::optional<SliceKey> playlistKey(not_null<const Data*> data) const; + bool validPlaylist(not_null<const Data*> data) const; void validatePlaylist(not_null<Data*> data); + std::optional<SliceKey> playlistOtherKey( + not_null<const Data*> data) const; + bool validOtherPlaylist(not_null<const Data*> data) const; + void validateOtherPlaylist(not_null<Data*> data); void playlistUpdated(not_null<Data*> data); bool moveInPlaylist(not_null<Data*> data, int delta, bool autonext); HistoryItem *itemByIndex(not_null<Data*> data, int index); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 96f513df4..0b149b973 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -593,7 +593,7 @@ void Widget::updateRepeatToggleIcon() { (active ? &st::mediaPlayerReverseIcon : &st::mediaPlayerReverseDisabledIcon), - active ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); + active ? nullptr : &st::mediaPlayerReverseDisabledIconOver); break; case OrderMode::Shuffle: _repeatToggle->setIconOverride( From 969b27cb7504aae46fd163123ee72047322303c5 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 18:53:18 +0400 Subject: [PATCH 063/180] Many-day selection by two clicks in the calendar. --- .../SourceFiles/ui/boxes/calendar_box.cpp | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 7cc728044..6740291de 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -367,6 +367,7 @@ private: const style::CalendarSizes &_st; const not_null<Context*> _context; + bool _twoPressSelectionStarted = false; std::map<int, std::unique_ptr<RippleAnimation>> _ripples; @@ -464,7 +465,7 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { if (selectedInRow > 0) { auto hq = PainterHighQualityEnabler(p); p.setPen(Qt::NoPen); - p.setBrush(st::lightButtonBgOver); + p.setBrush(st::dialogsRippleBgActive); p.drawRoundedRect( (x + (selectedFrom - index) * _st.cellSize.width() @@ -495,27 +496,24 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { p.setBrush(Qt::NoBrush); } const auto it = _ripples.find(index); - if (it != _ripples.cend()) { - const auto colorOverride = [&] { - if (selectionMode) { - return st::lightButtonBgOver; - } else if (highlighted) { - return grayedOut ? st::windowBgRipple : st::dialogsRippleBgActive; - } - return st::windowBgOver; - }()->c; + if (it != _ripples.cend() && !selectionMode) { + const auto colorOverride = (!highlighted + ? st::windowBgOver + : grayedOut + ? st::windowBgRipple + : st::dialogsRippleBgActive)->c; it->second->paint(p, innerLeft, innerTop, width(), &colorOverride); if (it->second->empty()) { _ripples.erase(it); } } - if (highlighted) { - p.setPen(grayedOut ? st::windowSubTextFg : st::dialogsNameFgActive); - } else if (enabled) { - p.setPen(grayedOut ? st::windowSubTextFg : st::boxTextFg); - } else { - p.setPen(st::windowSubTextFg); - } + p.setPen(selected + ? st::dialogsNameFgActive + : highlighted + ? (grayedOut ? st::windowSubTextFg : st::dialogsNameFgActive) + : enabled + ? (grayedOut ? st::windowSubTextFg : st::boxTextFg) + : st::windowSubTextFg); p.drawText(rect, _context->labelFromIndex(index), style::al_center); } } @@ -598,10 +596,15 @@ void CalendarBox::Inner::mousePressEvent(QMouseEvent *e) { if (_context->selectionMode()) { if (_context->selectedMin().has_value() - && (e->modifiers() & Qt::ShiftModifier)) { + && ((e->modifiers() & Qt::ShiftModifier) + || (_twoPressSelectionStarted + && (_context->selectedMin() + == _context->selectedMax())))) { _context->updateSelection(_selected); + _twoPressSelectionStarted = false; } else { _context->startSelection(_selected); + _twoPressSelectionStarted = true; } } } From 0309eb023ecd6aa814c71b14de6d2ec31bb2137a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 19:27:10 +0400 Subject: [PATCH 064/180] Cycle through repeat modes by repeat toggle button. --- .../media/player/media_player.style | 12 -- .../media/player/media_player_instance.cpp | 45 +++---- .../media/player/media_player_instance.h | 45 +------ .../player/media_player_repeat_controls.cpp | 10 -- .../media/player/media_player_widget.cpp | 117 ++++++------------ 5 files changed, 61 insertions(+), 168 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 3a3392c55..22a726790 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -97,21 +97,9 @@ mediaPlayerShuffleDisabledIconOver: icon { mediaPlayerRepeatReverseIcon: icon { { "player/player_repeat_reverse", mediaPlayerActiveFg, point(9px, 11px)} }; -mediaPlayerRepeatReverseDisabledIcon: icon { - { "player/player_repeat_reverse", menuIconFg, point(9px, 11px)} -}; -mediaPlayerRepeatReverseDisabledIconOver: icon { - { "player/player_repeat_reverse", menuIconFgOver, point(9px, 11px)} -}; mediaPlayerRepeatShuffleIcon: icon { { "player/player_repeat_shuffle", mediaPlayerActiveFg, point(9px, 11px)} }; -mediaPlayerRepeatShuffleDisabledIcon: icon { - { "player/player_repeat_shuffle", menuIconFg, point(9px, 11px)} -}; -mediaPlayerRepeatShuffleDisabledIconOver: icon { - { "player/player_repeat_shuffle", menuIconFgOver, point(9px, 11px)} -}; mediaPlayerRepeatDisabledRippleBg: windowBgOver; mediaPlayerSpeedButton: IconButton { diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index f32825002..4fe8f80a9 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -121,6 +121,13 @@ Instance::Instance() handleSongUpdate(audioId); }); + _songData.repeat.changes( + ) | rpl::start_with_next([=](RepeatMode mode) { + if (mode == RepeatMode::All) { + refreshPlaylist(&_songData); + } + }, _lifetime); + using namespace rpl::mappers; rpl::combine( Core::App().calls().currentCallValue(), @@ -254,7 +261,13 @@ void Instance::clearStreamed(not_null<Data*> data, bool savePosition) { void Instance::refreshPlaylist(not_null<Data*> data) { if (!validPlaylist(data)) { validatePlaylist(data); - } else if (!validOtherPlaylist(data)) { + } else { + refreshOtherPlaylist(data); + } +} + +void Instance::refreshOtherPlaylist(not_null<Data*> data) { + if (!validOtherPlaylist(data)) { validateOtherPlaylist(data); } playlistUpdated(data); @@ -322,12 +335,12 @@ void Instance::validatePlaylist(not_null<Data*> data) { ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) { data->playlistSlice = std::move(update); data->playlistSliceKey = key; - playlistUpdated(data); + refreshOtherPlaylist(data); }, data->playlistLifetime); } else { data->playlistSlice = std::nullopt; data->playlistSliceKey = data->playlistRequestedKey = std::nullopt; - playlistUpdated(data); + refreshOtherPlaylist(data); } } @@ -456,7 +469,14 @@ bool Instance::moveInPlaylist( }; const auto newIndex = *data->playlistIndex + (data->order.current() == OrderMode::Reverse ? -delta : delta); - if (const auto item = itemByIndex(data, newIndex)) { + const auto useIndex = (!data->playlistSlice + || data->playlistSlice->skippedAfter() != 0 + || data->playlistSlice->skippedBefore() != 0 + || !data->playlistSlice->size()) + ? newIndex + : ((newIndex + int(data->playlistSlice->size())) + % int(data->playlistSlice->size())); + if (const auto item = itemByIndex(data, useIndex)) { return jumpByItem(item); } else if (data->repeat.current() == RepeatMode::All && data->playlistOtherSlice @@ -740,23 +760,6 @@ void Instance::playPauseCancelClicked(AudioMsgId::Type type) { } } -void Instance::setRepeatMode(AudioMsgId::Type type, RepeatMode mode) { - if (const auto data = getData(type)) { - const auto otherNeeded = (mode == RepeatMode::All) - && (data->repeat.current() != RepeatMode::All); - data->repeat = mode; - if (otherNeeded) { - refreshPlaylist(data); - } - } -} - -void Instance::setOrderMode(AudioMsgId::Type type, OrderMode mode) { - if (const auto data = getData(type)) { - data->order = mode; - } -} - void Instance::startSeeking(AudioMsgId::Type type) { if (auto data = getData(type)) { data->seeking = data->current; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index d5f129bbd..bb39322a6 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -116,50 +116,6 @@ public: return AudioMsgId(); } - [[nodiscard]] RepeatMode repeatMode(AudioMsgId::Type type) const { - if (const auto data = getData(type)) { - return data->repeat.current(); - } - return RepeatMode::None; - } - [[nodiscard]] rpl::producer<RepeatMode> repeatModeValue( - AudioMsgId::Type type) const { - if (const auto data = getData(type)) { - return data->repeat.value(); - } - return rpl::single(RepeatMode::None); - } - [[nodiscard]] rpl::producer<RepeatMode> repeatModeChanges( - AudioMsgId::Type type) const { - if (const auto data = getData(type)) { - return data->repeat.changes(); - } - return rpl::never<RepeatMode>(); - } - void setRepeatMode(AudioMsgId::Type type, RepeatMode mode); - - [[nodiscard]] OrderMode orderMode(AudioMsgId::Type type) const { - if (const auto data = getData(type)) { - return data->order.current(); - } - return OrderMode::Default; - } - [[nodiscard]] rpl::producer<OrderMode> orderModeValue( - AudioMsgId::Type type) const { - if (const auto data = getData(type)) { - return data->order.value(); - } - return rpl::single(OrderMode::Default); - } - [[nodiscard]] rpl::producer<OrderMode> orderModeChanges( - AudioMsgId::Type type) const { - if (const auto data = getData(type)) { - return data->order.changes(); - } - return rpl::never<OrderMode>(); - } - void setOrderMode(AudioMsgId::Type type, OrderMode mode); - [[nodiscard]] bool isSeeking(AudioMsgId::Type type) const { if (const auto data = getData(type)) { return (data->seeking == data->current); @@ -268,6 +224,7 @@ private: void setCurrent(const AudioMsgId &audioId); void refreshPlaylist(not_null<Data*> data); + void refreshOtherPlaylist(not_null<Data*> data); std::optional<SliceKey> playlistKey(not_null<const Data*> data) const; bool validPlaylist(not_null<const Data*> data) const; void validatePlaylist(not_null<Data*> data); diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp index 302852dd0..3eb372d8e 100644 --- a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp +++ b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp @@ -70,22 +70,12 @@ void PrepareRepeatDropdown(not_null<Dropdown*> dropdown) { auto &settings = Core::App().settings(); const auto active = (settings.playerRepeatMode() == mode); settings.setPlayerRepeatMode(active ? RepeatMode::None : mode); - const auto type = AudioMsgId::Type::Song; - instance()->setRepeatMode(type, settings.playerRepeatMode()); - if (!active) { - instance()->setOrderMode(type, settings.playerOrderMode()); - } Core::App().saveSettingsDelayed(); }; const auto toggleOrder = [](OrderMode mode) { auto &settings = Core::App().settings(); const auto active = (settings.playerOrderMode() == mode); settings.setPlayerOrderMode(active ? OrderMode::Default : mode); - const auto type = AudioMsgId::Type::Song; - instance()->setOrderMode(type, settings.playerOrderMode()); - if (!active) { - instance()->setRepeatMode(type, settings.playerRepeatMode()); - } Core::App().saveSettingsDelayed(); }; repeatOne->setClickedCallback([=] { toggleRepeat(RepeatMode::One); }); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 0b149b973..2d016c0c0 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -292,31 +292,22 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) rpl::combine( Core::App().settings().playerRepeatModeValue(), - Core::App().settings().playerOrderModeValue(), - instance()->repeatModeValue(AudioMsgId::Type::Song), - instance()->orderModeValue(AudioMsgId::Type::Song) + Core::App().settings().playerOrderModeValue() ) | rpl::start_with_next([=] { updateRepeatToggleIcon(); }, lifetime()); + _repeatToggle->setClickedCallback([=] { - const auto type = AudioMsgId::Type::Song; - const auto repeat = Core::App().settings().playerRepeatMode(); - const auto order = Core::App().settings().playerOrderMode(); - const auto mayBeActive = (repeat != RepeatMode::None) - || (order != OrderMode::Default); - const auto active = mayBeActive - && (repeat == instance()->repeatMode(type)) - && (order == instance()->orderMode(type)); - if (!active && !mayBeActive) { - Core::App().settings().setPlayerRepeatMode(RepeatMode::All); - Core::App().saveSettingsDelayed(); - } - instance()->setRepeatMode(type, active - ? RepeatMode::None - : mayBeActive - ? repeat - : RepeatMode::All); - instance()->setOrderMode(type, active ? OrderMode::Default : order); + auto &settings = Core::App().settings(); + settings.setPlayerRepeatMode([&] { + switch (settings.playerRepeatMode()) { + case RepeatMode::None: return RepeatMode::One; + case RepeatMode::One: return RepeatMode::All; + case RepeatMode::All: return RepeatMode::None; + } + Unexpected("Repeat mode in Settings."); + }()); + Core::App().saveSettingsDelayed(); }); _playbackSpeed->saved( @@ -577,70 +568,34 @@ void Widget::updateRepeatToggleIcon() { const auto type = AudioMsgId::Type::Song; const auto repeat = Core::App().settings().playerRepeatMode(); const auto order = Core::App().settings().playerOrderMode(); - const auto active = (repeat == instance()->repeatMode(type)) - && (order == instance()->orderMode(type)) - && (repeat != RepeatMode::None || order != OrderMode::Default); - switch (repeat) { - case RepeatMode::None: - switch (order) { - case OrderMode::Default: - _repeatToggle->setIconOverride( - &st::mediaPlayerRepeatDisabledIcon, - &st::mediaPlayerRepeatDisabledIconOver); - break; - case OrderMode::Reverse: - _repeatToggle->setIconOverride( - (active - ? &st::mediaPlayerReverseIcon - : &st::mediaPlayerReverseDisabledIcon), - active ? nullptr : &st::mediaPlayerReverseDisabledIconOver); - break; - case OrderMode::Shuffle: - _repeatToggle->setIconOverride( - (active - ? &st::mediaPlayerShuffleIcon - : &st::mediaPlayerShuffleDisabledIcon), - active ? nullptr : &st::mediaPlayerShuffleDisabledIconOver); - break; - } - break; - case RepeatMode::One: + if (repeat == RepeatMode::None && order == OrderMode::Default) { _repeatToggle->setIconOverride( - (active - ? &st::mediaPlayerRepeatOneIcon - : &st::mediaPlayerRepeatOneDisabledIcon), - active ? nullptr : &st::mediaPlayerRepeatOneDisabledIconOver); - break; - case RepeatMode::All: - switch (order) { - case OrderMode::Default: - _repeatToggle->setIconOverride( - (active ? nullptr : &st::mediaPlayerRepeatDisabledIcon), - (active ? nullptr : &st::mediaPlayerRepeatDisabledIconOver)); + &st::mediaPlayerRepeatDisabledIcon, + &st::mediaPlayerRepeatDisabledIconOver); + _repeatToggle->setRippleColorOverride( + &st::mediaPlayerRepeatDisabledRippleBg); + return; + } + const auto &icon = [&]() -> const style::icon& { + switch (repeat) { + case RepeatMode::None: + switch (order) { + case OrderMode::Reverse: return st::mediaPlayerReverseIcon; + case OrderMode::Shuffle: return st::mediaPlayerShuffleIcon; + } break; - case OrderMode::Reverse: - _repeatToggle->setIconOverride( - (active - ? &st::mediaPlayerRepeatReverseIcon - : &st::mediaPlayerRepeatReverseDisabledIcon), - (active - ? nullptr - : &st::mediaPlayerRepeatReverseDisabledIconOver)); - break; - case OrderMode::Shuffle: - _repeatToggle->setIconOverride( - (active - ? &st::mediaPlayerRepeatShuffleIcon - : &st::mediaPlayerRepeatShuffleDisabledIcon), - (active - ? nullptr - : &st::mediaPlayerRepeatShuffleDisabledIconOver)); + case RepeatMode::One: return st::mediaPlayerRepeatOneIcon; + case RepeatMode::All: + switch (order) { + case OrderMode::Default: return st::mediaPlayerRepeatButton.icon; + case OrderMode::Reverse: return st::mediaPlayerRepeatReverseIcon; + case OrderMode::Shuffle: return st::mediaPlayerRepeatShuffleIcon; + } break; } - break; - } - _repeatToggle->setRippleColorOverride( - active ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); + Unexpected("Repeat / order values in Settings."); + }(); + _repeatToggle->setIconOverride(&icon); } void Widget::checkForTypeChange() { From 7d899525413376d05642ad250e84a9f3c863140d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 20:49:17 +0400 Subject: [PATCH 065/180] Edit noforwards in group / channel type. --- Telegram/Resources/langs/lang.strings | 4 +- .../boxes/peers/edit_peer_info_box.cpp | 54 ++++++----------- .../boxes/peers/edit_peer_type_box.cpp | 60 +++++++++++++++---- .../boxes/peers/edit_peer_type_box.h | 6 +- .../media/player/media_player_instance.cpp | 6 ++ 5 files changed, 77 insertions(+), 53 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index df03deaf6..514c9027b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1004,6 +1004,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_public_group_title" = "Public"; "lng_manage_private_peer_title" = "Private"; "lng_manage_public_peer_title" = "Public"; +"lng_manage_peer_no_forwards_title" = "Saving content"; +"lng_manage_peer_no_forwards" = "Restrict saving content"; +"lng_manage_peer_no_forwards_about" = "Participants won't be able to forward messages from this channel or save media files."; "lng_manage_discussion_group" = "Discussion"; "lng_manage_discussion_group_add" = "Add a group"; @@ -1825,7 +1828,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_contact_title" = "Edit contact name"; "lng_edit_channel_title" = "Edit channel"; "lng_edit_sign_messages" = "Sign messages"; -"lng_edit_allow_forwards" = "Allow saving content"; "lng_edit_group" = "Edit group"; "lng_edit_self_title" = "Edit your name"; "lng_confirm_contact_data" = "New Contact"; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index e7c32c8ba..98f9b4e31 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -277,7 +277,7 @@ private: std::optional<QString> description; std::optional<bool> hiddenPreHistory; std::optional<bool> signatures; - std::optional<bool> forwards; + std::optional<bool> noForwards; std::optional<ChannelData*> linkedChat; }; @@ -297,7 +297,6 @@ private: void fillLinkedChatButton(); //void fillInviteLinkButton(); void fillSignaturesButton(); - void fillForwardsButton(); void fillHistoryVisibilityButton(); void fillManageSection(); void fillPendingRequestsButton(); @@ -345,7 +344,7 @@ private: std::optional<HistoryVisibility> _historyVisibilitySavedValue; std::optional<QString> _usernameSavedValue; std::optional<bool> _signaturesSavedValue; - std::optional<bool> _forwardsSavedValue; + std::optional<bool> _noForwardsSavedValue; const not_null<Window::SessionNavigation*> _navigation; const not_null<Ui::BoxContent*> _box; @@ -611,10 +610,11 @@ void Controller::refreshHistoryVisibility() { void Controller::showEditPeerTypeBox( std::optional<rpl::producer<QString>> error) { const auto boxCallback = crl::guard(this, [=]( - Privacy checked, QString publicLink) { + Privacy checked, QString publicLink, bool noForwards) { _privacyTypeUpdates.fire(std::move(checked)); _privacySavedValue = checked; _usernameSavedValue = publicLink; + _noForwardsSavedValue = noForwards; refreshHistoryVisibility(); }); _navigation->parentController()->show( @@ -624,6 +624,7 @@ void Controller::showEditPeerTypeBox( boxCallback, _privacySavedValue, _usernameSavedValue, + _noForwardsSavedValue, error), Ui::LayerOption::KeepOther); } @@ -699,6 +700,7 @@ void Controller::fillPrivacyTypeButton() { && _peer->asChannel()->hasUsername()) ? Privacy::HasUsername : Privacy::NoUsername; + _noForwardsSavedValue = !_peer->allowsForwarding(); const auto isGroup = (_peer->isChat() || _peer->isMegagroup()); AddButtonWithText( @@ -800,21 +802,6 @@ void Controller::fillSignaturesButton() { }, _controls.buttonsLayout->lifetime()); } -void Controller::fillForwardsButton() { - Expects(_controls.buttonsLayout != nullptr); - - AddButtonWithText( - _controls.buttonsLayout, - tr::lng_edit_allow_forwards(), - rpl::single(QString()), - [=] {} - )->toggleOn(rpl::single(_peer->allowsForwarding()) - )->toggledValue( - ) | rpl::start_with_next([=](bool toggled) { - _forwardsSavedValue = toggled; - }, _controls.buttonsLayout->lifetime()); -} - void Controller::fillHistoryVisibilityButton() { Expects(_controls.buttonsLayout != nullptr); @@ -874,19 +861,16 @@ void Controller::fillManageSection() { const auto isChannel = (!chat); if (!chat && !channel) return; - const auto canEditUsername = [&] { + const auto canEditType = [&] { return isChannel - ? channel->canEditUsername() - : chat->canEditUsername(); + ? channel->amCreator() + : chat->amCreator(); }(); const auto canEditSignatures = [&] { return isChannel ? (channel->canEditSignatures() && !channel->isMegagroup()) : false; }(); - const auto canEditForwards = [&] { - return isChannel ? channel->amCreator() : chat->amCreator(); - }(); const auto canEditPreHistoryHidden = [&] { return isChannel ? channel->canEditPreHistoryHidden() @@ -946,7 +930,7 @@ void Controller::fillManageSection() { AddSkip(_controls.buttonsLayout, 0); - if (canEditUsername) { + if (canEditType) { fillPrivacyTypeButton(); //} else if (canEditInviteLinks) { // fillInviteLinkButton(); @@ -960,15 +944,11 @@ void Controller::fillManageSection() { if (canEditSignatures) { fillSignaturesButton(); } - if (canEditForwards) { - fillForwardsButton(); - } if (canEditPreHistoryHidden || canEditSignatures - || canEditForwards //|| canEditInviteLinks || canViewOrEditLinkedChat - || canEditUsername) { + || canEditType) { AddSkip( _controls.buttonsLayout, st::editPeerTopButtonsLayoutSkip, @@ -989,7 +969,7 @@ void Controller::fillManageSection() { st::infoIconPermissions); } if (canEditInviteLinks - && (canEditUsername + && (canEditType || !_peer->isChannel() || !_peer->asChannel()->hasUsername())) { auto count = Info::Profile::MigratedOrMeValue( @@ -1258,10 +1238,10 @@ bool Controller::validateSignatures(Saving &to) const { } bool Controller::validateForwards(Saving &to) const { - if (!_forwardsSavedValue.has_value()) { + if (!_noForwardsSavedValue.has_value()) { return true; } - to.forwards = _forwardsSavedValue; + to.noForwards = _noForwardsSavedValue; return true; } @@ -1537,13 +1517,13 @@ void Controller::saveSignatures() { } void Controller::saveForwards() { - if (!_savingData.forwards - || *_savingData.forwards == _peer->allowsForwarding()) { + if (!_savingData.noForwards + || *_savingData.noForwards != _peer->allowsForwarding()) { return continueSave(); } _api.request(MTPmessages_ToggleNoForwards( _peer->input, - MTP_bool(!*_savingData.forwards) + MTP_bool(*_savingData.noForwards) )).done([=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); continueSave(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index ac777e5eb..8740691f2 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -63,13 +63,14 @@ public: not_null<PeerData*> peer, bool useLocationPhrases, std::optional<Privacy> privacySavedValue, - std::optional<QString> usernameSavedValue); + std::optional<QString> usernameSavedValue, + std::optional<bool> noForwardsSavedValue); void createContent(); - QString getUsernameInput(); + [[nodiscard]] QString getUsernameInput() const; void setFocusUsername(); - rpl::producer<QString> getTitle() { + [[nodiscard]] rpl::producer<QString> getTitle() const { return !_privacySavedValue ? tr::lng_create_invite_link_title() : _isGroup @@ -77,14 +78,18 @@ public: : tr::lng_manage_peer_channel_type(); } - bool isAllowSave() { + [[nodiscard]] bool isAllowSave() { return _isAllowSave; } - Privacy getPrivacy() { + [[nodiscard]] Privacy getPrivacy() const { return _controls.privacy->value(); } + [[nodiscard]] bool noForwards() const { + return _controls.noForwards->toggled(); + } + void showError(rpl::producer<QString> text) { _controls.usernameInput->showError(); showUsernameError(std::move(text)); @@ -100,6 +105,8 @@ private: Ui::SlideWrap<Ui::RpWidget> *inviteLinkWrap = nullptr; Ui::FlatLabel *inviteLink = nullptr; + + Ui::SettingsButton *noForwards = nullptr; }; Controls _controls; @@ -133,6 +140,7 @@ private: MTP::Sender _api; std::optional<Privacy> _privacySavedValue; std::optional<QString> _usernameSavedValue; + std::optional<bool> _noForwardsSavedValue; bool _useLocationPhrases = false; bool _isGroup = false; @@ -153,12 +161,14 @@ Controller::Controller( not_null<PeerData*> peer, bool useLocationPhrases, std::optional<Privacy> privacySavedValue, - std::optional<QString> usernameSavedValue) + std::optional<QString> usernameSavedValue, + std::optional<bool> noForwardsSavedValue) : _peer(peer) , _linkOnly(!privacySavedValue.has_value()) , _api(&_peer->session().mtp()) , _privacySavedValue(privacySavedValue) , _usernameSavedValue(usernameSavedValue) +, _noForwardsSavedValue(noForwardsSavedValue) , _useLocationPhrases(useLocationPhrases) , _isGroup(_peer->isChat() || _peer->isMegagroup()) , _isAllowSave(!_usernameSavedValue.value_or(QString()).isEmpty()) @@ -196,6 +206,25 @@ void Controller::createContent() { AddSkip(_wrap.get()); AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); + AddSkip(_wrap.get()); + AddSubsectionTitle(_wrap.get(), tr::lng_manage_peer_no_forwards_title()); + _controls.noForwards = _wrap->add(EditPeerInfoBox::CreateButton( + _wrap.get(), + tr::lng_manage_peer_no_forwards(), + rpl::single(QString()), + [=] {}, + st::manageGroupTopButtonWithText, + nullptr + )); + _controls.noForwards->toggleOn( + rpl::single(_noForwardsSavedValue.value_or(false)) + )->toggledValue( + ) | rpl::start_with_next([=](bool toggled) { + _noForwardsSavedValue = toggled; + }, _wrap->lifetime()); + AddSkip(_wrap.get()); + AddDividerText(_wrap.get(), tr::lng_manage_peer_no_forwards_about()); + if (_linkOnly) { _controls.inviteLinkWrap->show(anim::type::instant); } else { @@ -299,7 +328,7 @@ void Controller::setFocusUsername() { } } -QString Controller::getUsernameInput() { +QString Controller::getUsernameInput() const { return _controls.usernameInput->getLastText().trimmed(); } @@ -575,22 +604,24 @@ EditPeerTypeBox::EditPeerTypeBox( QWidget*, not_null<PeerData*> peer, bool useLocationPhrases, - std::optional<FnMut<void(Privacy, QString)>> savedCallback, + std::optional<FnMut<void(Privacy, QString, bool)>> savedCallback, std::optional<Privacy> privacySaved, std::optional<QString> usernameSaved, + std::optional<bool> noForwardsValue, std::optional<rpl::producer<QString>> usernameError) : _peer(peer) , _useLocationPhrases(useLocationPhrases) , _savedCallback(std::move(savedCallback)) , _privacySavedValue(privacySaved) , _usernameSavedValue(usernameSaved) +, _noForwardsValue(noForwardsValue) , _usernameError(usernameError) { } EditPeerTypeBox::EditPeerTypeBox( QWidget*, not_null<PeerData*> peer) -: EditPeerTypeBox(nullptr, peer, {}, {}, {}, {}, {}) { +: EditPeerTypeBox(nullptr, peer, {}, {}, {}, {}, {}, {}) { } void EditPeerTypeBox::setInnerFocus() { @@ -608,7 +639,8 @@ void EditPeerTypeBox::prepare() { _peer, _useLocationPhrases, _privacySavedValue, - _usernameSavedValue); + _usernameSavedValue, + _noForwardsValue); _focusRequests.events( ) | rpl::start_with_next( [=] { @@ -632,10 +664,12 @@ void EditPeerTypeBox::prepare() { } auto local = std::move(*_savedCallback); - local(v, - (v == Privacy::HasUsername) + local( + v, + (v == Privacy::HasUsername ? controller->getUsernameInput() - : QString()); // We don't need username with private type. + : QString()), + controller->noForwards()); // We don't need username with private type. closeBox(); }); addButton(tr::lng_cancel(), [=] { closeBox(); }); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h index 0d4a828b1..fa7744125 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.h @@ -36,9 +36,10 @@ public: QWidget*, not_null<PeerData*> peer, bool useLocationPhrases, - std::optional<FnMut<void(Privacy, QString)>> savedCallback, + std::optional<FnMut<void(Privacy, QString, bool)>> savedCallback, std::optional<Privacy> privacySaved, std::optional<QString> usernameSaved, + std::optional<bool> noForwardsSaved, std::optional<rpl::producer<QString>> usernameError = {}); // For invite link only. @@ -53,10 +54,11 @@ protected: private: not_null<PeerData*> _peer; bool _useLocationPhrases = false; - std::optional<FnMut<void(Privacy, QString)>> _savedCallback; + std::optional<FnMut<void(Privacy, QString, bool)>> _savedCallback; std::optional<Privacy> _privacySavedValue; std::optional<QString> _usernameSavedValue; + std::optional<bool> _noForwardsValue; std::optional<rpl::producer<QString>> _usernameError; rpl::event_stream<> _focusRequests; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 4fe8f80a9..6ecda9263 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -467,6 +467,10 @@ bool Instance::moveInPlaylist( const auto jumpById = [&](FullMsgId id) { return jumpByItem(data->history->owner().message(id)); }; + + if (data->order.current() == OrderMode::Shuffle) { + } + const auto newIndex = *data->playlistIndex + (data->order.current() == OrderMode::Reverse ? -delta : delta); const auto useIndex = (!data->playlistSlice @@ -499,6 +503,7 @@ bool Instance::previousAvailable(AudioMsgId::Type type) const { return false; } else if (data->repeat.current() == RepeatMode::All) { return true; + } else if (data->order.current() == OrderMode::Shuffle) { } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex + 1 < data->playlistSlice->size()) @@ -513,6 +518,7 @@ bool Instance::nextAvailable(AudioMsgId::Type type) const { return false; } else if (data->repeat.current() == RepeatMode::All) { return true; + } else if (data->order.current() == OrderMode::Shuffle) { } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex > 0) From 4f7c7286327e8909d5f0b2102f22f4f470320317 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 19 Nov 2021 23:04:40 +0400 Subject: [PATCH 066/180] Update API scheme on layer 135. --- Telegram/Resources/tl/api.tl | 19 +++--- Telegram/SourceFiles/api/api_updates.cpp | 6 +- Telegram/SourceFiles/apiwrap.cpp | 60 +++++++++++-------- Telegram/SourceFiles/apiwrap.h | 5 +- .../SourceFiles/boxes/delete_messages_box.cpp | 2 +- .../SourceFiles/boxes/sticker_set_box.cpp | 5 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 11 +++- .../chat_helpers/stickers_dice_pack.cpp | 5 +- .../chat_helpers/stickers_emoji_pack.cpp | 10 +++- .../chat_helpers/stickers_list_widget.cpp | 30 ++++++---- .../SourceFiles/data/data_file_origin.cpp | 1 + Telegram/SourceFiles/data/data_user.cpp | 1 - .../data/stickers/data_stickers.cpp | 43 +++++++------ .../SourceFiles/data/stickers/data_stickers.h | 4 +- .../export/data/export_data_types.cpp | 15 +++-- .../export/data/export_data_types.h | 2 +- .../SourceFiles/export/export_api_wrap.cpp | 17 +++--- Telegram/SourceFiles/main/main_session.cpp | 7 +-- Telegram/SourceFiles/main/main_session.h | 2 +- 19 files changed, 141 insertions(+), 104 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index f2a919806..139cc03b4 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -222,7 +222,7 @@ inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings; -peerSettings#733f2961 flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true geo_distance:flags.6?int = PeerSettings; +peerSettings#a8228d2e flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true geo_distance:flags.6?int request_chat:flags.9?Peer request_chat_date:flags.9?int = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; wallPaperNoFile#e0804116 id:long flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; @@ -236,7 +236,7 @@ inputReportReasonCopyright#9b89f93a = ReportReason; inputReportReasonGeoIrrelevant#dbd4feed = ReportReason; inputReportReasonFake#f5ddd6e7 = ReportReason; -userFull#d697ff05 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string = UserFull; +userFull#cf366521 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true id:long about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string = UserFull; contact#145ade0b user_id:long mutual:Bool = Contact; @@ -568,6 +568,7 @@ inputStickerSetAnimatedEmojiAnimations#cde3739 = InputStickerSet; stickerSet#d7df217a flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumbs:flags.4?Vector<PhotoSize> thumb_dc_id:flags.4?int thumb_version:flags.4?int count:int hash:int = StickerSet; messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet; +messages.stickerSetNotModified#d3f924eb = messages.StickerSet; botCommand#c27ac8c7 command:string description:string = BotCommand; @@ -1292,6 +1293,10 @@ messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResult channels.sendAsPeers#8356cda9 peers:Vector<Peer> chats:Vector<Chat> users:Vector<User> = channels.SendAsPeers; +users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> = users.UserFull; + +messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Vector<User> = messages.PeerSettings; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1397,7 +1402,7 @@ account.setAuthorizationTTL#bf899aa0 authorization_ttl_days:int = Bool; account.changeAuthorizationSettings#432910d5 hash:long encrypted_requests_disabled:Bool = Bool; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; -users.getFullUser#ca30a5b1 id:InputUser = UserFull; +users.getFullUser#b60f5918 id:InputUser = users.UserFull; users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool; contacts.getContactIDs#7adc669d hash:long = Vector<int>; @@ -1434,7 +1439,7 @@ messages.sendMessage#d9d75a4 flags:# no_webpage:flags.1?true silent:flags.5?true messages.sendMedia#e25ff8e0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.forwardMessages#cc30290b flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int send_as:flags.13?InputPeer = Updates; messages.reportSpam#cf1592db peer:InputPeer = Bool; -messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings; +messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings; messages.report#8953ab4e peer:InputPeer id:Vector<int> reason:ReportReason message:string = Bool; messages.getChats#49e9528f id:Vector<long> = messages.Chats; messages.getFullChat#aeb00b34 chat_id:long = messages.ChatFull; @@ -1461,7 +1466,7 @@ messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vect messages.exportChatInvite#a02ce5d5 flags:# legacy_revoke_permanent:flags.2?true request_needed:flags.3?true peer:InputPeer expire_date:flags.0?int usage_limit:flags.1?int title:flags.4?string = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; -messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; +messages.getStickerSet#c8a0ec74 stickerset:InputStickerSet hash:int = messages.StickerSet; messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = messages.StickerSetInstallResult; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates; @@ -1614,8 +1619,7 @@ help.getCountriesList#735787a8 lang_code:string hash:int = help.CountriesList; channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool; channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages; -channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory; -channels.reportSpam#fe087810 channel:InputChannel user_id:InputUser id:Vector<int> = Bool; +channels.reportSpam#f44a8315 channel:InputChannel participant:InputPeer id:Vector<int> = Bool; channels.getMessages#ad8c9a23 channel:InputChannel id:Vector<InputMessage> = messages.Messages; channels.getParticipants#77ced9d0 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:long = channels.ChannelParticipants; channels.getParticipant#a0ab6cc6 channel:InputChannel participant:InputPeer = channels.ChannelParticipant; @@ -1651,6 +1655,7 @@ channels.convertToGigagroup#b290c69 channel:InputChannel = Updates; channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool; channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages; channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers; +channels.deleteParticipantHistory#367544db channel:InputChannel participant:InputPeer = messages.AffectedHistory; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 5dd2d8744..68701250f 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -2214,7 +2214,11 @@ void Updates::feedUpdate(const MTPUpdate &update) { ////// Cloud sticker sets case mtpc_updateNewStickerSet: { const auto &d = update.c_updateNewStickerSet(); - session().data().stickers().newSetReceived(d.vstickerset()); + d.vstickerset().match([&](const MTPDmessages_stickerSet &data) { + session().data().stickers().newSetReceived(data); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); + }); } break; case mtpc_updateStickerSetsOrder: { diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 750c522aa..bdf844f47 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1040,7 +1040,11 @@ void ApiWrap::requestFullPeer(not_null<PeerData*> peer) { } return request(MTPusers_GetFullUser( user->inputUser - )).done([=](const MTPUserFull &result, mtpRequestId requestId) { + )).done([=](const MTPusers_UserFull &result, mtpRequestId requestId) { + result.match([&](const MTPDusers_userFull &data) { + _session->data().processUsers(data.vusers()); + _session->data().processChats(data.vchats()); + }); gotUserFull(user, result, requestId); }).fail(failHandler).send(); } else if (const auto chat = peer->asChat()) { @@ -1072,12 +1076,6 @@ void ApiWrap::processFullPeer( gotChatFull(peer, result, mtpRequestId(0)); } -void ApiWrap::processFullPeer( - not_null<UserData*> user, - const MTPUserFull &result) { - gotUserFull(user, result, mtpRequestId(0)); -} - void ApiWrap::gotChatFull( not_null<PeerData*> peer, const MTPmessages_ChatFull &result, @@ -1117,18 +1115,20 @@ void ApiWrap::gotChatFull( void ApiWrap::gotUserFull( not_null<UserData*> user, - const MTPUserFull &result, + const MTPusers_UserFull &result, mtpRequestId req) { - const auto &d = result.c_userFull(); - if (user == _session->user() && !_session->validateSelf(d.vuser())) { - constexpr auto kRequestUserAgainTimeout = crl::time(10000); - base::call_delayed(kRequestUserAgainTimeout, _session, [=] { - requestFullPeer(user); + result.match([&](const MTPDusers_userFull &data) { + data.vfull_user().match([&](const MTPDuserFull &fields) { + if (user == _session->user() && !_session->validateSelf(fields.vid().v)) { + constexpr auto kRequestUserAgainTimeout = crl::time(10000); + base::call_delayed(kRequestUserAgainTimeout, _session, [=] { + requestFullPeer(user); + }); + return; + } + Data::ApplyUserUpdate(user, fields); }); - return; - } - Data::ApplyUserUpdate(user, d); - + }); if (req) { const auto i = _fullPeerRequests.find(user); if (i != _fullPeerRequests.cend() && i.value() == req) { @@ -1184,9 +1184,13 @@ void ApiWrap::requestPeerSettings(not_null<PeerData*> peer) { } request(MTPmessages_GetPeerSettings( peer->input - )).done([=](const MTPPeerSettings &result) { - peer->setSettings(result); - _requestedPeerSettings.erase(peer); + )).done([=](const MTPmessages_PeerSettings &result) { + result.match([&](const MTPDmessages_peerSettings &data) { + _session->data().processUsers(data.vusers()); + _session->data().processChats(data.vchats()); + peer->setSettings(data.vsettings()); + _requestedPeerSettings.erase(peer); + }); }).fail([=](const MTP::Error &error) { _requestedPeerSettings.erase(peer); }).send(); @@ -1775,9 +1779,9 @@ void ApiWrap::deleteAllFromUser( void ApiWrap::deleteAllFromUserSend( not_null<ChannelData*> channel, not_null<UserData*> from) { - request(MTPchannels_DeleteUserHistory( + request(MTPchannels_DeleteParticipantHistory( channel->inputChannel, - from->inputUser + from->input )).done([=](const MTPmessages_AffectedHistory &result) { const auto offset = applyAffectedHistory(channel, result); if (offset > 0) { @@ -1834,7 +1838,8 @@ void ApiWrap::requestStickerSets() { MTP_long(i.key()), MTP_long(i.value().first)); i.value().second = request(MTPmessages_GetStickerSet( - id + id, + MTP_int(0) // hash )).done([=, setId = i.key()](const MTPmessages_StickerSet &result) { gotStickerSet(setId, result); }).fail([=, setId = i.key()](const MTP::Error &error) { @@ -2589,7 +2594,11 @@ void ApiWrap::applyNotifySettings( void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) { _stickerSetRequests.remove(setId); - _session->data().stickers().feedSetFull(result); + result.match([&](const MTPDmessages_stickerSet &data) { + _session->data().stickers().feedSetFull(data); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); + }); } void ApiWrap::requestWebPageDelayed(WebPageData *page) { @@ -2846,7 +2855,8 @@ void ApiWrap::refreshFileReference( request(MTPmessages_GetStickerSet( MTP_inputStickerSetID( MTP_long(data.setId), - MTP_long(data.accessHash))), + MTP_long(data.accessHash)), + MTP_int(0)), // hash [=] { crl::on_main(_session, [=] { local().writeInstalledStickers(); local().writeRecentStickers(); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 6fced4cc2..f8fdbe4ca 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -210,9 +210,6 @@ public: void processFullPeer( not_null<PeerData*> peer, const MTPmessages_ChatFull &result); - void processFullPeer( - not_null<UserData*> user, - const MTPUserFull &result); void migrateChat( not_null<ChatData*> chat, @@ -459,7 +456,7 @@ private: mtpRequestId req); void gotUserFull( not_null<UserData*> user, - const MTPUserFull &result, + const MTPusers_UserFull &result, mtpRequestId req); void applyLastParticipantsList( not_null<ChannelData*> channel, diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.cpp b/Telegram/SourceFiles/boxes/delete_messages_box.cpp index f099b3bca..e1c5050ba 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/delete_messages_box.cpp @@ -487,7 +487,7 @@ void DeleteMessagesBox::deleteAndClear() { _moderateInChannel->session().api().request( MTPchannels_ReportSpam( _moderateInChannel->inputChannel, - _moderateFrom->inputUser, + _moderateFrom->input, MTP_vector<MTPint>(1, MTP_int(_ids[0].msg))) ).send(); } diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 7aacd11fd..6e66ce602 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -367,7 +367,8 @@ StickerSetBox::Inner::Inner( , _input(set) , _previewTimer([=] { showPreview(); }) { _api.request(MTPmessages_GetStickerSet( - Data::InputStickerSet(_input) + Data::InputStickerSet(_input), + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { gotSet(result); }).fail([=](const MTP::Error &error) { @@ -463,6 +464,8 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) { set->setThumbnail(_setThumbnail); } }); + }, [&](const MTPDmessages_stickerSetNotModified &data) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); if (_pack.isEmpty()) { diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index fcdab0848..ce2774c99 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -1887,11 +1887,16 @@ void StickersBox::Inner::handleMegagroupSetAddressChange() { } } else if (!_megagroupSetRequestId) { _megagroupSetRequestId = _api.request(MTPmessages_GetStickerSet( - MTP_inputStickerSetShortName(MTP_string(text)) + MTP_inputStickerSetShortName(MTP_string(text)), + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { _megagroupSetRequestId = 0; - auto set = session().data().stickers().feedSetFull(result); - setMegagroupSelectedSet(set->identifier()); + result.match([&](const MTPDmessages_stickerSet &data) { + const auto set = session().data().stickers().feedSetFull(data); + setMegagroupSelectedSet(set->identifier()); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); + }); }).fail([=](const MTP::Error &error) { _megagroupSetRequestId = 0; setMegagroupSelectedSet({}); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp index 4c2335b55..b1fb1a35f 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp @@ -47,10 +47,13 @@ void DicePack::load() { return; } _requestId = _session->api().request(MTPmessages_GetStickerSet( - MTP_inputStickerSetDice(MTP_string(_emoji)) + MTP_inputStickerSetDice(MTP_string(_emoji)), + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { result.match([&](const MTPDmessages_stickerSet &data) { applySet(data); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); }).fail([=](const MTP::Error &error) { _requestId = 0; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index b00db95ba..fee492880 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -231,12 +231,15 @@ void EmojiPack::refresh() { return; } _requestId = _session->api().request(MTPmessages_GetStickerSet( - MTP_inputStickerSetAnimatedEmoji() + MTP_inputStickerSetAnimatedEmoji(), + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { _requestId = 0; refreshAnimations(); result.match([&](const MTPDmessages_stickerSet &data) { applySet(data); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); }).fail([=](const MTP::Error &error) { _requestId = 0; @@ -249,12 +252,15 @@ void EmojiPack::refreshAnimations() { return; } _animationsRequestId = _session->api().request(MTPmessages_GetStickerSet( - MTP_inputStickerSetAnimatedEmojiAnimations() + MTP_inputStickerSetAnimatedEmojiAnimations(), + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { _animationsRequestId = 0; refreshDelayed(); result.match([&](const MTPDmessages_stickerSet &data) { applyAnimationsSet(data); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); }).fail([=](const MTP::Error &error) { _animationsRequestId = 0; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 9db3b019a..c907c5f5e 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -2778,16 +2778,21 @@ void StickersListWidget::refreshMegagroupStickers(GroupStickersPlace place) { } _megagroupSetIdRequested = set.id; _api.request(MTPmessages_GetStickerSet( - Data::InputStickerSet(set) + Data::InputStickerSet(set), + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { - if (const auto set = session().data().stickers().feedSetFull(result)) { - refreshStickers(); - if (set->id == _megagroupSetIdRequested) { - _megagroupSetIdRequested = 0; - } else { - LOG(("API Error: Got different set.")); + result.match([&](const MTPDmessages_stickerSet &data) { + if (const auto set = session().data().stickers().feedSetFull(data)) { + refreshStickers(); + if (set->id == _megagroupSetIdRequested) { + _megagroupSetIdRequested = 0; + } else { + LOG(("API Error: Got different set.")); + } } - } + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); + }); }).send(); } @@ -3117,9 +3122,14 @@ void StickersListWidget::installSet(uint64 setId) { const auto input = set->mtpInput(); if ((set->flags & SetFlag::NotLoaded) || set->stickers.empty()) { _api.request(MTPmessages_GetStickerSet( - input + input, + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { - session().data().stickers().feedSetFull(result); + result.match([&](const MTPDmessages_stickerSet &data) { + session().data().stickers().feedSetFull(data); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); + }); sendInstallRequest(setId, input); }).send(); } else { diff --git a/Telegram/SourceFiles/data/data_file_origin.cpp b/Telegram/SourceFiles/data/data_file_origin.cpp index 4eeca4535..f55cd470f 100644 --- a/Telegram/SourceFiles/data/data_file_origin.cpp +++ b/Telegram/SourceFiles/data/data_file_origin.cpp @@ -140,6 +140,7 @@ struct FileReferenceAccumulator { void push(const MTPmessages_StickerSet &data) { data.match([&](const MTPDmessages_stickerSet &data) { push(data.vdocuments()); + }, [](const MTPDmessages_stickerSetNotModified &data) { }); } void push(const MTPmessages_SavedGifs &data) { diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 43aa88743..bf714b4e8 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -188,7 +188,6 @@ bool UserData::hasCalls() const { namespace Data { void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) { - user->owner().processUser(update.vuser()); if (const auto photo = update.vprofile_photo()) { user->owner().processPhoto(*photo); } diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index 09293c429..86ed5b3a4 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -518,25 +518,27 @@ void Stickers::requestSetToPushFaved(not_null<DocumentData*> document) { setIsFaved(document, std::move(list)); }; session().api().request(MTPmessages_GetStickerSet( - Data::InputStickerSet(document->sticker()->set) + Data::InputStickerSet(document->sticker()->set), + MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { - Expects(result.type() == mtpc_messages_stickerSet); - - auto list = std::vector<not_null<EmojiPtr>>(); - auto &d = result.c_messages_stickerSet(); - list.reserve(d.vpacks().v.size()); - for (const auto &mtpPack : d.vpacks().v) { - auto &pack = mtpPack.c_stickerPack(); - for (const auto &documentId : pack.vdocuments().v) { - if (documentId.v == document->id) { - if (const auto emoji = Ui::Emoji::Find(qs(mtpPack.c_stickerPack().vemoticon()))) { - list.emplace_back(emoji); + result.match([&](const MTPDmessages_stickerSet &data) { + auto list = std::vector<not_null<EmojiPtr>>(); + list.reserve(data.vpacks().v.size()); + for (const auto &mtpPack : data.vpacks().v) { + auto &pack = mtpPack.c_stickerPack(); + for (const auto &documentId : pack.vdocuments().v) { + if (documentId.v == document->id) { + if (const auto emoji = Ui::Emoji::Find(qs(mtpPack.c_stickerPack().vemoticon()))) { + list.emplace_back(emoji); + } + break; } - break; } } - } - addAnyway(std::move(list)); + addAnyway(std::move(list)); + }, [](const MTPDmessages_stickerSetNotModified &) { + LOG(("API Error: Unexpected messages.stickerSetNotModified.")); + }); }).fail([=](const MTP::Error &error) { // Perhaps this is a deleted sticker pack. Add anyway. addAnyway({}); @@ -1230,11 +1232,9 @@ StickersSet *Stickers::feedSet(const MTPDstickerSet &data) { return it->second.get(); } -StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) { - Expects(data.type() == mtpc_messages_stickerSet); - Expects(data.c_messages_stickerSet().vset().type() == mtpc_stickerSet); +StickersSet *Stickers::feedSetFull(const MTPDmessages_stickerSet &d) { + Expects(d.vset().type() == mtpc_stickerSet); - const auto &d = data.c_messages_stickerSet(); const auto &s = d.vset().c_stickerSet(); auto &sets = setsRef(); @@ -1353,8 +1353,7 @@ StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) { return set; } -void Stickers::newSetReceived(const MTPmessages_StickerSet &data) { - const auto &set = data.c_messages_stickerSet(); +void Stickers::newSetReceived(const MTPDmessages_stickerSet &set) { const auto &s = set.vset().c_stickerSet(); if (!s.vinstalled_date()) { LOG(("API Error: " @@ -1374,7 +1373,7 @@ void Stickers::newSetReceived(const MTPmessages_StickerSet &data) { order.insert(insertAtIndex, s.vid().v); } - feedSetFull(data); + feedSetFull(set); } QString Stickers::getSetTitle(const MTPDstickerSet &s) { diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.h b/Telegram/SourceFiles/data/stickers/data_stickers.h index 3550e4a44..b381e0b49 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.h +++ b/Telegram/SourceFiles/data/stickers/data_stickers.h @@ -198,8 +198,8 @@ public: not_null<DocumentData*> document); StickersSet *feedSet(const MTPDstickerSet &data); - StickersSet *feedSetFull(const MTPmessages_StickerSet &data); - void newSetReceived(const MTPmessages_StickerSet &data); + StickersSet *feedSetFull(const MTPDmessages_stickerSet &d); + void newSetReceived(const MTPDmessages_stickerSet &set); QString getSetTitle(const MTPDstickerSet &s); diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 3410d7403..2048feb41 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -1283,15 +1283,14 @@ std::map<MessageId, Message> ParseMessagesList( return result; } -PersonalInfo ParsePersonalInfo(const MTPUserFull &data) { - Expects(data.type() == mtpc_userFull); - - const auto &fields = data.c_userFull(); +PersonalInfo ParsePersonalInfo(const MTPDusers_userFull &data) { auto result = PersonalInfo(); - result.user = ParseUser(fields.vuser()); - if (const auto about = fields.vabout()) { - result.bio = ParseString(*about); - } + result.user = ParseUser(data.vusers().v[0]); + data.vfull_user().match([&](const MTPDuserFull &data) { + if (const auto about = data.vabout()) { + result.bio = ParseString(*about); + } + }); return result; } diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 677bfb3a8..495446814 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -255,7 +255,7 @@ struct PersonalInfo { Utf8String bio; }; -PersonalInfo ParsePersonalInfo(const MTPUserFull &data); +PersonalInfo ParsePersonalInfo(const MTPDusers_userFull &data); struct TopPeer { Peer peer; diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp index bd62779cc..464f7ff90 100644 --- a/Telegram/SourceFiles/export/export_api_wrap.cpp +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -674,15 +674,14 @@ void ApiWrap::startMainSession(FnMut<void()> done) { void ApiWrap::requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done) { mainRequest(MTPusers_GetFullUser( _user - )).done([=, done = std::move(done)](const MTPUserFull &result) mutable { - Expects(result.type() == mtpc_userFull); - - const auto &full = result.c_userFull(); - if (full.vuser().type() == mtpc_user) { - done(Data::ParsePersonalInfo(result)); - } else { - error("Bad user type."); - } + )).done([=, done = std::move(done)](const MTPusers_UserFull &result) mutable { + result.match([&](const MTPDusers_userFull &data) { + if (!data.vusers().v.empty()) { + done(Data::ParsePersonalInfo(data)); + } else { + error("Bad user type."); + } + }); }).send(); } diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 4507d75d1..b329e30bc 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -220,11 +220,8 @@ PeerId Session::userPeerId() const { return _user->id; } -bool Session::validateSelf(const MTPUser &user) { - if (user.type() != mtpc_user || !user.c_user().is_self()) { - LOG(("API Error: bad self user received.")); - return false; - } else if (UserId(user.c_user().vid()) != userId()) { +bool Session::validateSelf(UserId id) { + if (id != userId()) { LOG(("Auth Error: wrong self user received.")); crl::on_main(this, [=] { _account->logOut(); }); return false; diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 44693c498..004a15b4d 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -82,7 +82,7 @@ public: [[nodiscard]] not_null<UserData*> user() const { return _user; } - bool validateSelf(const MTPUser &user); + bool validateSelf(UserId id); [[nodiscard]] Data::Changes &changes() const { return *_changes; From 596c4a06a3c7c7660c494144c5a9b1731286aa68 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Nov 2021 13:07:14 +0400 Subject: [PATCH 067/180] Use button colors for the calendar selections. --- Telegram/SourceFiles/ui/boxes/calendar_box.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index 6740291de..b6ec11030 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -465,7 +465,7 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { if (selectedInRow > 0) { auto hq = PainterHighQualityEnabler(p); p.setPen(Qt::NoPen); - p.setBrush(st::dialogsRippleBgActive); + p.setBrush(st::activeButtonBg); p.drawRoundedRect( (x + (selectedFrom - index) * _st.cellSize.width() @@ -508,7 +508,7 @@ void CalendarBox::Inner::paintRows(Painter &p, QRect clip) { } } p.setPen(selected - ? st::dialogsNameFgActive + ? st::activeButtonFg : highlighted ? (grayedOut ? st::windowSubTextFg : st::dialogsNameFgActive) : enabled From 4cb6e4b787d5fa83d63fa4c458f16802f74638f3 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Nov 2021 13:32:04 +0400 Subject: [PATCH 068/180] Allow group / channel type box if !can_edit_username. --- .../boxes/peers/edit_peer_type_box.cpp | 49 ++++++++----------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 8740691f2..44d9e5741 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -206,25 +206,26 @@ void Controller::createContent() { AddSkip(_wrap.get()); AddDividerText(_wrap.get(), tr::lng_group_invite_manage_about()); - AddSkip(_wrap.get()); - AddSubsectionTitle(_wrap.get(), tr::lng_manage_peer_no_forwards_title()); - _controls.noForwards = _wrap->add(EditPeerInfoBox::CreateButton( - _wrap.get(), - tr::lng_manage_peer_no_forwards(), - rpl::single(QString()), - [=] {}, - st::manageGroupTopButtonWithText, - nullptr - )); - _controls.noForwards->toggleOn( - rpl::single(_noForwardsSavedValue.value_or(false)) - )->toggledValue( - ) | rpl::start_with_next([=](bool toggled) { - _noForwardsSavedValue = toggled; - }, _wrap->lifetime()); - AddSkip(_wrap.get()); - AddDividerText(_wrap.get(), tr::lng_manage_peer_no_forwards_about()); - + if (!_linkOnly) { + AddSkip(_wrap.get()); + AddSubsectionTitle(_wrap.get(), tr::lng_manage_peer_no_forwards_title()); + _controls.noForwards = _wrap->add(EditPeerInfoBox::CreateButton( + _wrap.get(), + tr::lng_manage_peer_no_forwards(), + rpl::single(QString()), + [=] {}, + st::manageGroupTopButtonWithText, + nullptr + )); + _controls.noForwards->toggleOn( + rpl::single(_noForwardsSavedValue.value_or(false)) + )->toggledValue( + ) | rpl::start_with_next([=](bool toggled) { + _noForwardsSavedValue = toggled; + }, _wrap->lifetime()); + AddSkip(_wrap.get()); + AddDividerText(_wrap.get(), tr::lng_manage_peer_no_forwards_about()); + } if (_linkOnly) { _controls.inviteLinkWrap->show(anim::type::instant); } else { @@ -267,15 +268,7 @@ void Controller::addRoundButton( void Controller::fillPrivaciesButtons( not_null<Ui::VerticalLayout*> parent, std::optional<Privacy> savedValue) { - const auto canEditUsername = [&] { - if (const auto chat = _peer->asChat()) { - return chat->canEditUsername(); - } else if (const auto channel = _peer->asChannel()) { - return channel->canEditUsername(); - } - Unexpected("Peer type in Controller::createPrivaciesEdit."); - }(); - if (!canEditUsername || _linkOnly) { + if (_linkOnly) { return; } From bdc43c0aeaccf5e6bc7f88a38958162ff32ff173 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Nov 2021 16:48:49 +0400 Subject: [PATCH 069/180] Update API scheme on layer 135. Add channelAdminLogEventActionSendMessage admin log action. --- Telegram/Resources/langs/lang.strings | 9 +++++---- Telegram/Resources/tl/api.tl | 5 +++-- .../history/admin_log/history_admin_log_item.cpp | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 514c9027b..62b58354f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2497,14 +2497,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_forwards_disabled" = "{from} restricted content copying"; "lng_admin_log_history_made_hidden" = "{from} made group history hidden for new members"; "lng_admin_log_history_made_visible" = "{from} made group history visible for new members"; -"lng_admin_log_pinned_message" = "{from} pinned message:"; -"lng_admin_log_unpinned_message" = "{from} unpinned message"; +"lng_admin_log_pinned_message" = "{from} pinned this message:"; +"lng_admin_log_unpinned_message" = "{from} unpinned this message"; "lng_admin_log_edited_caption" = "{from} edited caption:"; "lng_admin_log_removed_caption" = "{from} removed caption"; "lng_admin_log_previous_caption" = "Original caption"; -"lng_admin_log_edited_message" = "{from} edited message:"; +"lng_admin_log_edited_message" = "{from} edited this message:"; "lng_admin_log_previous_message" = "Original message"; -"lng_admin_log_deleted_message" = "{from} deleted message:"; +"lng_admin_log_deleted_message" = "{from} deleted this message:"; +"lng_admin_log_sent_message" = "{from} sent this message:"; "lng_admin_log_participant_joined" = "{from} joined the group"; "lng_admin_log_participant_joined_channel" = "{from} joined the channel"; "lng_admin_log_participant_joined_by_link" = "{from} joined the group via {link}"; diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 139cc03b4..21aed824a 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -222,7 +222,7 @@ inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings; -peerSettings#a8228d2e flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true geo_distance:flags.6?int request_chat:flags.9?Peer request_chat_date:flags.9?int = PeerSettings; +peerSettings#a518110d flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true autoarchived:flags.7?true invite_members:flags.8?true request_chat_broadcast:flags.10?true geo_distance:flags.6?int request_chat_title:flags.9?string request_chat_date:flags.9?int = PeerSettings; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; wallPaperNoFile#e0804116 id:long flags:# default:flags.1?true dark:flags.4?true settings:flags.2?WallPaperSettings = WallPaper; @@ -916,12 +916,13 @@ channelAdminLogEventActionParticipantVolume#3e7f6847 participant:GroupCallPartic channelAdminLogEventActionChangeHistoryTTL#6e941a38 prev_value:int new_value:int = ChannelAdminLogEventAction; channelAdminLogEventActionParticipantJoinByRequest#afb6144a invite:ExportedChatInvite approved_by:long = ChannelAdminLogEventAction; channelAdminLogEventActionToggleNoForwards#cb2ac766 new_value:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionSendMessage#278f2868 message:Message = ChannelAdminLogEventAction; channelAdminLogEvent#1fad68cd id:long date:int user_id:long action:ChannelAdminLogEventAction = ChannelAdminLogEvent; channels.adminLogResults#ed8af74d events:Vector<ChannelAdminLogEvent> chats:Vector<Chat> users:Vector<User> = channels.AdminLogResults; -channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true = ChannelAdminLogEventsFilter; +channelAdminLogEventsFilter#ea107ae4 flags:# join:flags.0?true leave:flags.1?true invite:flags.2?true ban:flags.3?true unban:flags.4?true kick:flags.5?true unkick:flags.6?true promote:flags.7?true demote:flags.8?true info:flags.9?true settings:flags.10?true pinned:flags.11?true edit:flags.12?true delete:flags.13?true group_call:flags.14?true invites:flags.15?true send:flags.16?true = ChannelAdminLogEventsFilter; popularContact#5ce14175 client_id:long importers:int = PopularContact; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 7e3d8b69a..b98f177bd 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -1123,6 +1123,20 @@ void GenerateItems( addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; + auto createSendMessage = [&](const MTPDchannelAdminLogEventActionSendMessage &data) { + auto text = tr::lng_admin_log_sent_message(tr::now, lt_from, fromLinkText); + addSimpleServiceMessage(text); + + auto detachExistingItem = false; + addPart( + history->createItem( + history->nextNonHistoryEntryId(), + PrepareLogMessage(data.vmessage(), date), + MessageFlag::AdminLogEntry, + detachExistingItem), + ExtractSentDate(data.vmessage())); + }; + action.match([&](const MTPDchannelAdminLogEventActionChangeTitle &data) { createChangeTitle(data); }, [&](const MTPDchannelAdminLogEventActionChangeAbout &data) { @@ -1191,6 +1205,8 @@ void GenerateItems( createParticipantJoinByRequest(data); }, [&](const MTPDchannelAdminLogEventActionToggleNoForwards &data) { createToggleNoForwards(data); + }, [&](const MTPDchannelAdminLogEventActionSendMessage &data) { + createSendMessage(data); }); } From 8d66680a961b303b2e871773ec662f6a101a9357 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Nov 2021 20:59:44 +0400 Subject: [PATCH 070/180] Show info about request chat admin incoming message. --- Telegram/Resources/langs/lang.strings | 6 + Telegram/SourceFiles/data/data_peer.cpp | 9 +- Telegram/SourceFiles/data/data_peer.h | 19 +- .../SourceFiles/history/history_widget.cpp | 4 - Telegram/SourceFiles/history/history_widget.h | 2 - .../view/history_view_contact_status.cpp | 230 ++++++++++++++---- .../view/history_view_contact_status.h | 63 ++--- Telegram/SourceFiles/mainwidget.cpp | 7 - Telegram/SourceFiles/mainwidget.h | 1 - Telegram/SourceFiles/ui/chat/chat.style | 3 + 10 files changed, 244 insertions(+), 100 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 62b58354f..d72879dd7 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1537,6 +1537,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_new_contact_add_name" = "Add {user} to contacts"; "lng_new_contact_add_done" = "{user} is now in your contact list."; "lng_new_contact_unarchive" = "Unarchive"; +"lng_new_contact_from_request_channel" = "{user} is an admin of {name}, a channel you requested to join."; +"lng_new_contact_from_request_group" = "{user} is an admin of {name}, a group you requested to join."; +"lng_from_request_title_channel" = "Chat with channel's admin"; +"lng_from_request_title_group" = "Chat with group's admin"; +"lng_from_request_body" = "You received this message because you requested to join {name} on {date}."; +"lng_from_request_understand" = "I understand"; "lng_cant_send_to_not_contact" = "Sorry, you can only send messages to\nmutual contacts at the moment.\n{more_info}"; "lng_cant_invite_not_contact" = "Sorry, you can only add mutual contacts\nto groups at the moment.\n{more_info}"; "lng_cant_more_info" = "More info »"; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 29d5f01b2..0239f23a4 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -561,6 +561,9 @@ void PeerData::checkFolder(FolderId folderId) { void PeerData::setSettings(const MTPPeerSettings &data) { data.match([&](const MTPDpeerSettings &data) { + _requestChatTitle = data.vrequest_chat_title().value_or_empty(); + _requestChatDate = data.vrequest_chat_date().value_or_empty(); + using Flag = PeerSetting; setSettings((data.is_add_contact() ? Flag::AddContact : Flag()) | (data.is_autoarchived() ? Flag::AutoArchived : Flag()) @@ -571,7 +574,11 @@ void PeerData::setSettings(const MTPPeerSettings &data) { : Flag()) //| (data.is_report_geo() ? Flag::ReportGeo : Flag()) | (data.is_report_spam() ? Flag::ReportSpam : Flag()) - | (data.is_share_contact() ? Flag::ShareContact : Flag())); + | (data.is_share_contact() ? Flag::ShareContact : Flag()) + | (data.vrequest_chat_title() ? Flag::RequestChat : Flag()) + | (data.is_request_chat_broadcast() + ? Flag::RequestChatIsBroadcast + : Flag())); }); } diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 800ddf9d1..10e18379f 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -199,7 +199,9 @@ enum class PeerSetting { ShareContact = (1 << 3), NeedContactsException = (1 << 4), AutoArchived = (1 << 5), - Unknown = (1 << 6), + RequestChat = (1 << 6), + RequestChatIsBroadcast = (1 << 7), + Unknown = (1 << 8), }; inline constexpr bool is_flag_type(PeerSetting) { return true; }; using PeerSettings = base::flags<PeerSetting>; @@ -403,7 +405,7 @@ public: // Returns true if about text was changed. bool setAbout(const QString &newAbout); - const QString &about() const { + [[nodiscard]] const QString &about() const { return _about; } @@ -412,16 +414,22 @@ public: void setSettings(PeerSettings which) { _settings.set(which); } - auto settings() const { + [[nodiscard]] auto settings() const { return (_settings.current() & PeerSetting::Unknown) ? std::nullopt : std::make_optional(_settings.current()); } - auto settingsValue() const { + [[nodiscard]] auto settingsValue() const { return (_settings.current() & PeerSetting::Unknown) ? _settings.changes() : (_settings.value() | rpl::type_erased()); } + [[nodiscard]] QString requestChatTitle() const { + return _requestChatTitle; + } + [[nodiscard]] TimeId requestChatDate() const { + return _requestChatDate; + } void setSettings(const MTPPeerSettings &data); @@ -510,6 +518,9 @@ private: BlockStatus _blockStatus = BlockStatus::Unknown; LoadedStatus _loadedStatus = LoadedStatus::Not; + QString _requestChatTitle; + TimeId _requestChatDate = 0; + QString _about; QString _themeEmoticon; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 450fea245..fff5b2c9b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -7326,10 +7326,6 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { } } -QRect HistoryWidget::historyRect() const { - return _scroll->geometry(); -} - QPoint HistoryWidget::clampMousePosition(QPoint point) { if (point.x() < 0) { point.setX(0); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 30399fe9d..e546696be 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -157,8 +157,6 @@ public: void firstLoadMessages(); void delayedShowAt(MsgId showAtMsgId); - QRect historyRect() const; - void updateFieldPlaceholder(); bool updateStickersByEmoji(); diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp index 8fa55ad9a..a55325f44 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp @@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/text/format_values.h" // Ui::FormatPhone #include "ui/text/text_utilities.h" +#include "ui/boxes/confirm_box.h" +#include "ui/layers/generic_box.h" #include "data/data_peer.h" #include "data/data_user.h" #include "data/data_chat.h" @@ -26,10 +28,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "api/api_blocked_peers.h" #include "main/main_session.h" -#include "ui/boxes/confirm_box.h" +#include "base/unixtime.h" #include "boxes/peers/edit_contact_box.h" #include "styles/style_chat.h" #include "styles/style_layers.h" +#include "styles/style_info.h" namespace HistoryView { namespace { @@ -56,7 +59,73 @@ bool BarCurrentlyHidden(not_null<PeerData*> peer) { } // namespace -ContactStatus::Bar::Bar(QWidget *parent, const QString &name) +class ContactStatus::BgButton final : public Ui::RippleButton { +public: + BgButton(QWidget *parent, const style::FlatButton &st); + +protected: + void paintEvent(QPaintEvent *e) override; + + void onStateChanged(State was, StateChangeSource source) override; + +private: + const style::FlatButton &_st; + +}; + +class ContactStatus::Bar final : public Ui::RpWidget { +public: + Bar(QWidget *parent, const QString &name); + + void showState(State state); + + [[nodiscard]] rpl::producer<> unarchiveClicks() const; + [[nodiscard]] rpl::producer<> addClicks() const; + [[nodiscard]] rpl::producer<> blockClicks() const; + [[nodiscard]] rpl::producer<> shareClicks() const; + [[nodiscard]] rpl::producer<> reportClicks() const; + [[nodiscard]] rpl::producer<> closeClicks() const; + [[nodiscard]] rpl::producer<> requestInfoClicks() const; + +private: + int resizeGetHeight(int newWidth) override; + + QString _name; + object_ptr<Ui::FlatButton> _add; + object_ptr<Ui::FlatButton> _unarchive; + object_ptr<Ui::FlatButton> _block; + object_ptr<Ui::FlatButton> _share; + object_ptr<Ui::FlatButton> _report; + object_ptr<Ui::IconButton> _close; + object_ptr<BgButton> _requestChatBg; + object_ptr<Ui::FlatLabel> _requestChatInfo; + +}; + +ContactStatus::BgButton::BgButton( + QWidget *parent, + const style::FlatButton &st) +: RippleButton(parent, st.ripple) +, _st(st) { +} + +void ContactStatus::BgButton::onStateChanged( + State was, + StateChangeSource source) { + RippleButton::onStateChanged(was, source); + update(); +} + +void ContactStatus::BgButton::paintEvent(QPaintEvent *e) { + QPainter p(this); + + p.fillRect(e->rect(), isOver() ? _st.overBgColor : _st.bgColor); + paintRipple(p, 0, 0); +} + +ContactStatus::Bar::Bar( + QWidget *parent, + const QString &name) : RpWidget(parent) , _name(name) , _add( @@ -79,26 +148,46 @@ ContactStatus::Bar::Bar(QWidget *parent, const QString &name) this, QString(), st::historyContactStatusBlock) -, _close(this, st::historyReplyCancel) { - resize(_close->size()); +, _close(this, st::historyReplyCancel) +, _requestChatBg(this, st::historyContactStatusButton) +, _requestChatInfo( + this, + QString(), + st::historyContactStatusLabel) { + _requestChatInfo->setAttribute(Qt::WA_TransparentForMouseEvents); } void ContactStatus::Bar::showState(State state) { - _add->setVisible(state == State::AddOrBlock || state == State::Add); - _unarchive->setVisible(state == State::UnarchiveOrBlock - || state == State::UnarchiveOrReport); - _block->setVisible(state == State::AddOrBlock - || state == State::UnarchiveOrBlock); - _share->setVisible(state == State::SharePhoneNumber); - _report->setVisible(state == State::ReportSpam - || state == State::UnarchiveOrReport); - _add->setText((state == State::Add) + using Type = State::Type; + const auto type = state.type; + _add->setVisible(type == Type::AddOrBlock || type == Type::Add); + _unarchive->setVisible(type == Type::UnarchiveOrBlock + || type == Type::UnarchiveOrReport); + _block->setVisible(type == Type::AddOrBlock + || type == Type::UnarchiveOrBlock); + _share->setVisible(type == Type::SharePhoneNumber); + _close->setVisible(type != Type::RequestChatInfo); + _report->setVisible(type == Type::ReportSpam + || type == Type::UnarchiveOrReport); + _requestChatInfo->setVisible(type == Type::RequestChatInfo); + _requestChatBg->setVisible(type == Type::RequestChatInfo); + _add->setText((type == Type::Add) ? tr::lng_new_contact_add_name(tr::now, lt_user, _name).toUpper() : tr::lng_new_contact_add(tr::now).toUpper()); - _report->setText((state == State::ReportSpam) + _report->setText((type == Type::ReportSpam) ? tr::lng_report_spam_and_leave(tr::now).toUpper() : tr::lng_report_spam(tr::now).toUpper()); - updateButtonsGeometry(); + _requestChatInfo->setMarkedText( + (state.requestChatIsBroadcast + ? tr::lng_new_contact_from_request_channel + : tr::lng_new_contact_from_request_group)( + tr::now, + lt_user, + Ui::Text::Bold(_name), + lt_name, + Ui::Text::Bold(state.requestChatName), + Ui::Text::WithEntities)); + resizeToWidth(width()); } rpl::producer<> ContactStatus::Bar::unarchiveClicks() const { @@ -125,16 +214,19 @@ rpl::producer<> ContactStatus::Bar::closeClicks() const { return _close->clicks() | rpl::to_empty; } -void ContactStatus::Bar::resizeEvent(QResizeEvent *e) { - _close->moveToRight(0, 0); - updateButtonsGeometry(); +rpl::producer<> ContactStatus::Bar::requestInfoClicks() const { + return _requestChatBg->clicks() | rpl::to_empty; } -void ContactStatus::Bar::updateButtonsGeometry() { - const auto full = width(); +int ContactStatus::Bar::resizeGetHeight(int newWidth) { + _close->moveToRight(0, 0); + const auto closeWidth = _close->width(); - const auto available = full - closeWidth; + const auto available = newWidth - closeWidth; const auto skip = st::historyContactStatusMinSkip; + if (available <= 2 * skip) { + return _close->height(); + } const auto buttonWidth = [&](const object_ptr<Ui::FlatButton> &button) { return button->textWidth() + 2 * skip; }; @@ -157,18 +249,18 @@ void ContactStatus::Bar::updateButtonsGeometry() { thatWidth + closeWidth - available, 0, closeWidth); - placeButton(button, full, margin); + placeButton(button, newWidth, margin); }; const auto &leftButton = _unarchive->isHidden() ? _add : _unarchive; const auto &rightButton = _block->isHidden() ? _report : _block; if (!leftButton->isHidden() && !rightButton->isHidden()) { const auto leftWidth = buttonWidth(leftButton); const auto rightWidth = buttonWidth(rightButton); - const auto half = full / 2; + const auto half = newWidth / 2; if (leftWidth <= half - && rightWidth + 2 * closeWidth <= full - half) { + && rightWidth + 2 * closeWidth <= newWidth - half) { placeButton(leftButton, half); - placeButton(rightButton, full - half); + placeButton(rightButton, newWidth - half); } else if (leftWidth + rightWidth <= available) { const auto margin = std::clamp( leftWidth + rightWidth + closeWidth - available, @@ -177,22 +269,31 @@ void ContactStatus::Bar::updateButtonsGeometry() { const auto realBlockWidth = rightWidth + 2 * closeWidth - margin; if (leftWidth > realBlockWidth) { placeButton(leftButton, leftWidth); - placeButton(rightButton, full - leftWidth, margin); + placeButton(rightButton, newWidth - leftWidth, margin); } else { - placeButton(leftButton, full - realBlockWidth); + placeButton(leftButton, newWidth - realBlockWidth); placeButton(rightButton, realBlockWidth, margin); } } else { const auto forLeft = (available * leftWidth) / (leftWidth + rightWidth); placeButton(leftButton, forLeft); - placeButton(rightButton, full - forLeft, closeWidth); + placeButton(rightButton, newWidth - forLeft, closeWidth); } } else { placeOne(_add); placeOne(_share); placeOne(_report); } + if (_requestChatInfo->isHidden()) { + return _close->height(); + } + const auto vskip = st::topBarArrowPadding.top(); + _requestChatInfo->resizeToWidth(available - 2 * skip); + _requestChatInfo->move(skip, vskip); + const auto newHeight = _requestChatInfo->height() + 2 * vskip; + _requestChatBg->setGeometry(0, 0, newWidth, newHeight); + return newHeight; } ContactStatus::ContactStatus( @@ -231,6 +332,7 @@ void ContactStatus::setupWidgets(not_null<Ui::RpWidget*> parent) { auto ContactStatus::PeerState(not_null<PeerData*> peer) -> rpl::producer<State> { using SettingsChange = PeerData::Settings::Change; + using Type = State::Type; if (const auto user = peer->asUser()) { using FlagsChange = UserData::Flags::Change; using Flag = UserDataFlag; @@ -245,34 +347,43 @@ auto ContactStatus::PeerState(not_null<PeerData*> peer) user->settingsValue() ) | rpl::map([=]( FlagsChange flags, - SettingsChange settings) { + SettingsChange settings) -> State { if (flags.value & Flag::Blocked) { - return State::None; + return { Type::None }; } else if (user->isContact()) { if (settings.value & PeerSetting::ShareContact) { - return State::SharePhoneNumber; + return { Type::SharePhoneNumber }; } else { - return State::None; + return { Type::None }; } + } else if (settings.value & PeerSetting::RequestChat) { + return { + .type = Type::RequestChatInfo, + .requestChatName = peer->requestChatTitle(), + .requestChatIsBroadcast = !!(settings.value + & PeerSetting::RequestChatIsBroadcast), + .requestDate = peer->requestChatDate(), + }; } else if (settings.value & PeerSetting::AutoArchived) { - return State::UnarchiveOrBlock; + return { Type::UnarchiveOrBlock }; } else if (settings.value & PeerSetting::BlockContact) { - return State::AddOrBlock; + return { Type::AddOrBlock }; } else if (settings.value & PeerSetting::AddContact) { - return State::Add; + return { Type::Add }; } else { - return State::None; + return { Type::None }; } }); } return peer->settingsValue( ) | rpl::map([=](SettingsChange settings) { + using Type = State::Type; return (settings.value & PeerSetting::AutoArchived) - ? State::UnarchiveOrReport + ? State{ Type::UnarchiveOrReport } : (settings.value & PeerSetting::ReportSpam) - ? State::ReportSpam - : State::None; + ? State{ Type::ReportSpam } + : State{ Type::None }; }); } @@ -285,7 +396,7 @@ void ContactStatus::setupState(not_null<PeerData*> peer) { peer ) | rpl::start_with_next([=](State state) { _state = state; - if (state == State::None) { + if (state.type == State::Type::None) { _bar.hide(anim::type::normal); } else { _bar.entity()->showState(state); @@ -303,6 +414,7 @@ void ContactStatus::setupHandlers(not_null<PeerData*> peer) { setupUnarchiveHandler(peer); setupReportHandler(peer); setupCloseHandler(peer); + setupRequestInfoHandler(peer); } void ContactStatus::setupAddHandler(not_null<UserData*> user) { @@ -420,8 +532,44 @@ void ContactStatus::setupCloseHandler(not_null<PeerData*> peer) { }, _bar.lifetime()); } +void ContactStatus::setupRequestInfoHandler(not_null<PeerData*> peer) { + const auto request = _bar.lifetime().make_state<mtpRequestId>(0); + _bar.entity()->requestInfoClicks( + ) | rpl::filter([=] { + return !(*request); + }) | rpl::start_with_next([=] { + _controller->show(Box([=](not_null<Ui::GenericBox*> box) { + box->setTitle((_state.requestChatIsBroadcast + ? tr::lng_from_request_title_channel + : tr::lng_from_request_title_group)()); + + box->addRow(object_ptr<Ui::FlatLabel>( + box, + tr::lng_from_request_body( + lt_name, + rpl::single(Ui::Text::Bold(_state.requestChatName)), + lt_date, + rpl::single(langDateTimeFull( + base::unixtime::parse(_state.requestDate) + )) | Ui::Text::ToWithEntities(), + Ui::Text::WithEntities), + st::boxLabel)); + + box->addButton(tr::lng_from_request_understand(), [=] { + if (*request) { + return; + } + peer->setSettings(0); + *request = peer->session().api().request( + MTPmessages_HidePeerSettingsBar(peer->input) + ).send(); + }); + })); + }, _bar.lifetime()); +} + void ContactStatus::show() { - const auto visible = (_state != State::None); + const auto visible = (_state.type != State::Type::None); if (!_shown) { _shown = true; if (visible) { diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.h b/Telegram/SourceFiles/history/view/history_view_contact_status.h index 3e258555f..56b696fcf 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.h +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.h @@ -18,6 +18,7 @@ class SessionController; namespace Ui { class FlatButton; class IconButton; +class FlatLabel; } // namespace Ui namespace HistoryView { @@ -33,51 +34,32 @@ public: void raise(); void move(int x, int y); - int height() const; - rpl::producer<int> heightValue() const; + [[nodiscard]] int height() const; + [[nodiscard]] rpl::producer<int> heightValue() const; - rpl::lifetime &lifetime() { + [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } private: - enum class State { - None, - ReportSpam, - Add, - AddOrBlock, - UnarchiveOrBlock, - UnarchiveOrReport, - SharePhoneNumber, - }; - - class Bar : public Ui::RpWidget { - public: - Bar(QWidget *parent, const QString &name); - - void showState(State state); - - rpl::producer<> unarchiveClicks() const; - rpl::producer<> addClicks() const; - rpl::producer<> blockClicks() const; - rpl::producer<> shareClicks() const; - rpl::producer<> reportClicks() const; - rpl::producer<> closeClicks() const; - - protected: - void resizeEvent(QResizeEvent *e) override; - - private: - void updateButtonsGeometry(); - - QString _name; - object_ptr<Ui::FlatButton> _add; - object_ptr<Ui::FlatButton> _unarchive; - object_ptr<Ui::FlatButton> _block; - object_ptr<Ui::FlatButton> _share; - object_ptr<Ui::FlatButton> _report; - object_ptr<Ui::IconButton> _close; + class Bar; + class BgButton; + struct State { + enum class Type { + None, + ReportSpam, + Add, + AddOrBlock, + UnarchiveOrBlock, + UnarchiveOrReport, + SharePhoneNumber, + RequestChatInfo, + }; + Type type = Type::None; + QString requestChatName; + bool requestChatIsBroadcast = false; + TimeId requestDate = 0; }; void setupWidgets(not_null<Ui::RpWidget*> parent); @@ -89,11 +71,12 @@ private: void setupUnarchiveHandler(not_null<PeerData*> peer); void setupReportHandler(not_null<PeerData*> peer); void setupCloseHandler(not_null<PeerData*> peer); + void setupRequestInfoHandler(not_null<PeerData*> peer); static rpl::producer<State> PeerState(not_null<PeerData*> peer); const not_null<Window::SessionController*> _controller; - State _state = State::None; + State _state; Ui::SlideWrap<Bar> _bar; Ui::PlainShadow _shadow; bool _shown = false; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 5a4f77046..50fd731aa 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1857,13 +1857,6 @@ void MainWidget::orderWidgets() { if (_hider) _hider->raise(); } -QRect MainWidget::historyRect() const { - QRect r(_history->historyRect()); - r.moveLeft(r.left() + _history->x()); - r.moveTop(r.top() + _history->y()); - return r; -} - QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶ms) { QPixmap result; floatPlayerHideAll(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 91402ca22..87ce1a6e4 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -151,7 +151,6 @@ public: void showBackFromStack( const SectionShow ¶ms); void orderWidgets(); - QRect historyRect() const; QPixmap grabForShowAnimation(const Window::SectionSlideParams ¶ms); void checkMainSectionToLayer(); diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index d231ba3e2..795da8759 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -268,6 +268,9 @@ historyContactStatusBlock: FlatButton(historyContactStatusButton) { color: attentionButtonFg; overColor: attentionButtonFg; } +historyContactStatusLabel: FlatLabel(defaultFlatLabel) { + minWidth: 240px; +} historyContactStatusMinSkip: 16px; historySendIcon: icon {{ "chat/input_send", historySendIconFg }}; From 5e3b094e8625f9ebf3d034100a6f09458997fae7 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 22 Nov 2021 21:12:00 +0400 Subject: [PATCH 071/180] Allow report / block / delete all from channels. --- Telegram/SourceFiles/apiwrap.cpp | 16 ++++++++-------- Telegram/SourceFiles/apiwrap.h | 8 ++++---- .../SourceFiles/boxes/delete_messages_box.cpp | 4 ++-- Telegram/SourceFiles/boxes/delete_messages_box.h | 2 +- Telegram/SourceFiles/history/history.cpp | 8 ++++---- Telegram/SourceFiles/history/history.h | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index bdf844f47..6c7cd9cab 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1757,13 +1757,13 @@ void ApiWrap::unblockParticipant( _kickRequests.emplace(kick, requestId); } -void ApiWrap::deleteAllFromUser( +void ApiWrap::deleteAllFromParticipant( not_null<ChannelData*> channel, - not_null<UserData*> from) { + not_null<PeerData*> from) { const auto history = _session->data().historyLoaded(channel); const auto ids = history - ? history->collectMessagesFromUserToDelete(from) - : QVector<MsgId>(); + ? history->collectMessagesFromParticipantToDelete(from) + : std::vector<MsgId>(); const auto channelId = peerToChannel(channel->id); for (const auto &msgId : ids) { if (const auto item = _session->data().message(channelId, msgId)) { @@ -1773,19 +1773,19 @@ void ApiWrap::deleteAllFromUser( _session->data().sendHistoryChangeNotifications(); - deleteAllFromUserSend(channel, from); + deleteAllFromParticipantSend(channel, from); } -void ApiWrap::deleteAllFromUserSend( +void ApiWrap::deleteAllFromParticipantSend( not_null<ChannelData*> channel, - not_null<UserData*> from) { + not_null<PeerData*> from) { request(MTPchannels_DeleteParticipantHistory( channel->inputChannel, from->input )).done([=](const MTPmessages_AffectedHistory &result) { const auto offset = applyAffectedHistory(channel, result); if (offset > 0) { - deleteAllFromUserSend(channel, from); + deleteAllFromParticipantSend(channel, from); } else if (const auto history = _session->data().historyLoaded(channel)) { history->requestChatListMessage(); } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index f8fdbe4ca..e9d60ff0a 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -230,9 +230,9 @@ public: void unblockParticipant( not_null<ChannelData*> channel, not_null<PeerData*> participant); - void deleteAllFromUser( + void deleteAllFromParticipant( not_null<ChannelData*> channel, - not_null<UserData*> from); + not_null<PeerData*> from); void requestWebPageDelayed(WebPageData *page); void clearWebPageRequest(WebPageData *page); @@ -518,9 +518,9 @@ private: bool revoke); void applyAffectedMessages(const MTPmessages_AffectedMessages &result); - void deleteAllFromUserSend( + void deleteAllFromParticipantSend( not_null<ChannelData*> channel, - not_null<UserData*> from); + not_null<PeerData*> from); void uploadAlbumMedia( not_null<HistoryItem*> item, diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.cpp b/Telegram/SourceFiles/boxes/delete_messages_box.cpp index e1c5050ba..bcfd3ae27 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/delete_messages_box.cpp @@ -39,7 +39,7 @@ DeleteMessagesBox::DeleteMessagesBox( _moderateBan = item->suggestBanReport(); _moderateDeleteAll = item->suggestDeleteAllReport(); if (_moderateBan || _moderateDeleteAll) { - _moderateFrom = item->from()->asUser(); + _moderateFrom = item->from(); _moderateInChannel = item->history()->peer->asChannel(); } } @@ -492,7 +492,7 @@ void DeleteMessagesBox::deleteAndClear() { ).send(); } if (_deleteAll && _deleteAll->checked()) { - _moderateInChannel->session().api().deleteAllFromUser( + _moderateInChannel->session().api().deleteAllFromParticipant( _moderateInChannel, _moderateFrom); } diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.h b/Telegram/SourceFiles/boxes/delete_messages_box.h index 38bf7d38f..61b8773e7 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.h +++ b/Telegram/SourceFiles/boxes/delete_messages_box.h @@ -64,7 +64,7 @@ private: const QDate _wipeHistoryFirstToDelete; const QDate _wipeHistoryLastToDelete; const MessageIdsList _ids; - UserData *_moderateFrom = nullptr; + PeerData *_moderateFrom = nullptr; ChannelData *_moderateInChannel = nullptr; bool _moderateBan = false; bool _moderateDeleteAll = false; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 655415dc0..b1fffd857 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -3094,13 +3094,13 @@ bool History::removeOrphanMediaGroupPart() { return false; } -QVector<MsgId> History::collectMessagesFromUserToDelete( - not_null<UserData*> user) const { - auto result = QVector<MsgId>(); +std::vector<MsgId> History::collectMessagesFromParticipantToDelete( + not_null<PeerData*> participant) const { + auto result = std::vector<MsgId>(); for (const auto &block : blocks) { for (const auto &message : block->messages) { const auto item = message->data(); - if (item->from() == user && item->canDelete()) { + if (item->from() == participant && item->canDelete()) { result.push_back(item->id); } } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 9f2257a84..0b26a1d0a 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -101,8 +101,8 @@ public: Element *findLastDisplayed() const; bool hasOrphanMediaGroupPart() const; bool removeOrphanMediaGroupPart(); - QVector<MsgId> collectMessagesFromUserToDelete( - not_null<UserData*> user) const; + [[nodiscard]] std::vector<MsgId> collectMessagesFromParticipantToDelete( + not_null<PeerData*> participant) const; enum class ClearType { Unload, From 9bc9547b1c577614408a5f16fdb78b4485ab2157 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Nov 2021 15:33:53 +0400 Subject: [PATCH 072/180] Revert "Fix crash in imported messages in search." This reverts commit 65d96c0364532d0afd302a621943d8f745064497. --- Telegram/SourceFiles/history/history_item.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 58355a8c1..0ad52d455 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -982,13 +982,13 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const { }; if (options.hideSender || isPost() || isEmpty()) { return {}; - } else if (!_history->peer->isUser() || _history->peer->isSelf()) { + } else if (!_history->peer->isUser()) { + return fromSender(displayFrom()); + } else if (_history->peer->isSelf()) { if (const auto forwarded = Get<HistoryMessageForwarded>()) { return forwarded->originalSender ? fromSender(forwarded->originalSender) : forwarded->hiddenSenderInfo->name; - } else if (!_history->peer->isUser()) { - return fromSender(displayFrom()); } } return {}; From a7c9a1ab34529bc915b9d521c5ea6d943f97bf27 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Nov 2021 15:40:15 +0400 Subject: [PATCH 073/180] Fix chats list forwarded messages sender. --- Telegram/SourceFiles/history/history_item.cpp | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 0ad52d455..14af5c11e 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -974,22 +974,29 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const { } return {}; }(); + const auto fromSender = [](not_null<PeerData*> sender) { + return sender->isSelf() + ? tr::lng_from_you(tr::now) + : sender->shortName(); + }; + const auto fromForwarded = [&]() -> std::optional<QString> { + if (const auto forwarded = Get<HistoryMessageForwarded>()) { + return forwarded->originalSender + ? fromSender(forwarded->originalSender) + : forwarded->hiddenSenderInfo->name; + } + return {}; + }; const auto sender = [&]() -> std::optional<QString> { - const auto fromSender = [](not_null<PeerData*> sender) { - return sender->isSelf() - ? tr::lng_from_you(tr::now) - : sender->shortName(); - }; if (options.hideSender || isPost() || isEmpty()) { return {}; } else if (!_history->peer->isUser()) { - return fromSender(displayFrom()); - } else if (_history->peer->isSelf()) { - if (const auto forwarded = Get<HistoryMessageForwarded>()) { - return forwarded->originalSender - ? fromSender(forwarded->originalSender) - : forwarded->hiddenSenderInfo->name; + if (const auto from = displayFrom()) { + return fromSender(from); } + return fromForwarded(); + } else if (_history->peer->isSelf()) { + return fromForwarded(); } return {}; }(); From d1b9785d31792c97f5a99834ed5b4f117d0e4b55 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Nov 2021 15:55:40 +0400 Subject: [PATCH 074/180] Allow deleting all from a channel to a group. --- Telegram/SourceFiles/history/history_item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 14af5c11e..386efa430 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -737,7 +737,7 @@ bool HistoryItem::suggestDeleteAllReport() const { if (!channel || !channel->canDeleteMessages()) { return false; } - return !isPost() && !out() && from()->isUser(); + return !isPost() && !out(); } bool HistoryItem::hasDirectLink() const { From 5cd339332cb1e4e62d281625e6fc7447bb624a38 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Nov 2021 17:51:38 +0400 Subject: [PATCH 075/180] Implement shuffled playlist. --- .../media/player/media_player_instance.cpp | 199 ++++++++++++++++++ .../media/player/media_player_instance.h | 7 + 2 files changed, 206 insertions(+) diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 6ecda9263..2028f0201 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -9,8 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" +#include "data/data_changes.h" #include "data/data_streaming.h" #include "data/data_file_click_handler.h" +#include "base/random.h" #include "media/audio/media_audio.h" #include "media/audio/media_audio_capture.h" #include "media/streaming/media_streaming_instance.h" @@ -42,6 +44,8 @@ constexpr auto kIdsLimit = 32; // Preload next messages if we went further from current than that. constexpr auto kIdsPreloadAfter = 28; +constexpr auto kShufflePlaylistLimit = 10'000; + constexpr auto kMinLengthForSavePosition = 20 * TimeId(60); // 20 minutes. auto VoicePlaybackSpeed() { @@ -62,6 +66,21 @@ struct Instance::Streamed { rpl::lifetime lifetime; }; +struct Instance::ShuffleData { + using UniversalMsgId = MsgId; + + std::vector<UniversalMsgId> playlist; + std::vector<UniversalMsgId> nonPlayedIds; + std::vector<UniversalMsgId> playedIds; + History *history = nullptr; + History *migrated = nullptr; + bool scheduled = false; + int indexInPlayedIds = 0; + bool allLoaded = false; + rpl::lifetime nextSliceLifetime; + rpl::lifetime lifetime; +}; + void start(not_null<Audio::Instance*> instance) { Audio::Start(instance); Capture::Start(); @@ -277,8 +296,14 @@ void Instance::playlistUpdated(not_null<Data*> data) { if (data->playlistSlice) { const auto fullId = data->current.contextId(); data->playlistIndex = data->playlistSlice->indexOf(fullId); + if (data->order.current() == OrderMode::Shuffle) { + validateShuffleData(data); + } else { + data->shuffleData = nullptr; + } } else { data->playlistIndex = std::nullopt; + data->shuffleData = nullptr; } data->playlistChanges.fire({}); } @@ -469,6 +494,41 @@ bool Instance::moveInPlaylist( }; if (data->order.current() == OrderMode::Shuffle) { + const auto raw = data->shuffleData.get(); + if (!raw || !raw->history) { + return false; + } + const auto byUniversal = [&](ShuffleData::UniversalMsgId id) { + return (id < 0) + ? jumpById({ ChannelId(), id + ServerMaxMsgId }) + : jumpById({ raw->history->channelId(), id }); + }; + if (delta < 0) { + return (raw->indexInPlayedIds > 0) + && byUniversal(raw->playedIds[--raw->indexInPlayedIds]); + } else if (raw->indexInPlayedIds + 1 < raw->playedIds.size()) { + return byUniversal(raw->playedIds[++raw->indexInPlayedIds]); + } else { + if (raw->nonPlayedIds.empty()) { + return false; + } + if (const auto universal = computeCurrentUniversalId(data)) { + if (raw->nonPlayedIds.size() == 1 + && raw->nonPlayedIds.front() == universal) { + return false; + } + if (raw->indexInPlayedIds == raw->playedIds.size()) { + raw->playedIds.push_back(universal); + } + const auto i = ranges::find(raw->nonPlayedIds, universal); + if (i != end(raw->nonPlayedIds)) { + raw->nonPlayedIds.erase(i); + } + ++raw->indexInPlayedIds; + } + const auto index = base::RandomIndex(raw->nonPlayedIds.size()); + return byUniversal(raw->nonPlayedIds[index]); + } } const auto newIndex = *data->playlistIndex @@ -495,6 +555,22 @@ bool Instance::moveInPlaylist( return false; } +MsgId Instance::computeCurrentUniversalId(not_null<const Data*> data) const { + const auto raw = data->shuffleData.get(); + if (!raw) { + return MsgId(0); + } + const auto current = data->current.contextId(); + const auto item = raw->history->owner().message(current); + return !item + ? MsgId(0) + : (item->history() == raw->history) + ? item->id + : (item->history() == raw->migrated) + ? (item->id - ServerMaxMsgId) + : MsgId(0); +} + bool Instance::previousAvailable(AudioMsgId::Type type) const { const auto data = getData(type); Assert(data != nullptr); @@ -504,6 +580,8 @@ bool Instance::previousAvailable(AudioMsgId::Type type) const { } else if (data->repeat.current() == RepeatMode::All) { return true; } else if (data->order.current() == OrderMode::Shuffle) { + const auto raw = data->shuffleData.get(); + return raw && (raw->indexInPlayedIds > 0); } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex + 1 < data->playlistSlice->size()) @@ -519,6 +597,13 @@ bool Instance::nextAvailable(AudioMsgId::Type type) const { } else if (data->repeat.current() == RepeatMode::All) { return true; } else if (data->order.current() == OrderMode::Shuffle) { + const auto raw = data->shuffleData.get(); + const auto universal = computeCurrentUniversalId(data); + return raw + && ((raw->indexInPlayedIds + 1 < raw->playedIds.size()) + || (raw->nonPlayedIds.size() > 1) + || (!raw->nonPlayedIds.empty() + && raw->nonPlayedIds.front() != universal)); } return (data->order.current() == OrderMode::Reverse) ? (*data->playlistIndex > 0) @@ -689,6 +774,120 @@ void Instance::stopAndClear(not_null<Data*> data) { _tracksFinished.fire_copy(data->type); } +void Instance::validateShuffleData(not_null<Data*> data) { + if (!data->history) { + data->shuffleData = nullptr; + return; + } else if (!data->shuffleData) { + setupShuffleData(data); + } + const auto raw = data->shuffleData.get(); + const auto key = playlistKey(data); + const auto scheduled = key && key->scheduled; + if (raw->history != data->history + || raw->migrated != data->migrated + || raw->scheduled != scheduled) { + raw->history = data->history; + raw->migrated = data->migrated; + raw->scheduled = scheduled; + raw->nextSliceLifetime.destroy(); + raw->allLoaded = false; + raw->playlist.clear(); + raw->nonPlayedIds.clear(); + raw->playedIds.clear(); + raw->indexInPlayedIds = 0; + } else if (raw->allLoaded || raw->nextSliceLifetime) { + return; + } + if (raw->scheduled) { + const auto count = data->playlistSlice + ? int(data->playlistSlice->size()) + : 0; + if (raw->playlist.empty() && count > 0) { + raw->playlist.reserve(count); + for (auto i = 0; i != count; ++i) { + raw->playlist.push_back((*data->playlistSlice)[i].msg); + } + raw->nonPlayedIds = raw->playlist; + raw->allLoaded = true; + data->playlistChanges.fire({}); + } + return; + } + const auto last = raw->playlist.empty() + ? MsgId(ServerMaxMsgId - 1) + : raw->playlist.back(); + SharedMediaMergedViewer( + &raw->history->session(), + SharedMediaMergedKey( + SliceKey( + raw->history->peer->id, + raw->migrated ? raw->migrated->peer->id : 0, + last, + false), + data->overview), + kIdsLimit, + kIdsLimit + ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) { + raw->nextSliceLifetime.destroy(); + + const auto size = update.size(); + const auto channel = raw->history->channelId(); + raw->playlist.reserve(raw->playlist.size() + size); + raw->nonPlayedIds.reserve(raw->nonPlayedIds.size() + size); + for (auto i = size; i != 0;) { + const auto fullId = update[--i]; + const auto universal = (fullId.channel == channel) + ? fullId.msg + : (fullId.msg - ServerMaxMsgId); + if (raw->playlist.empty() || raw->playlist.back() > universal) { + raw->playlist.push_back(universal); + raw->nonPlayedIds.push_back(universal); + } + } + if (update.skippedBefore() == 0 + || raw->playlist.size() >= kShufflePlaylistLimit) { + raw->allLoaded = true; + } + data->playlistChanges.fire({}); + }, raw->nextSliceLifetime); +} + +void Instance::setupShuffleData(not_null<Data*> data) { + data->shuffleData = std::make_unique<ShuffleData>(); + const auto raw = data->shuffleData.get(); + data->history->session().changes().messageUpdates( + ::Data::MessageUpdate::Flag::Destroyed + ) | rpl::map([=](const ::Data::MessageUpdate &update) { + const auto item = update.item; + const auto history = item->history().get(); + return (history == raw->history) + ? item->id + : (history == raw->migrated) + ? (item->id - ServerMaxMsgId) + : MsgId(0); + }) | rpl::filter( + rpl::mappers::_1 != MsgId(0) + ) | rpl::start_with_next([=](MsgId id) { + const auto i = ranges::find(raw->playlist, id); + if (i != end(raw->playlist)) { + raw->playlist.erase(i); + } + const auto j = ranges::find(raw->nonPlayedIds, id); + if (j != end(raw->nonPlayedIds)) { + raw->nonPlayedIds.erase(j); + } + const auto k = ranges::find(raw->playedIds, id); + if (k != end(raw->playedIds)) { + const auto index = (k - begin(raw->playedIds)); + raw->playedIds.erase(k); + if (raw->indexInPlayedIds > index) { + --raw->indexInPlayedIds; + } + } + }, data->shuffleData->lifetime); +} + void Instance::playPause(AudioMsgId::Type type) { if (const auto data = getData(type)) { if (!data->streamed) { diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index bb39322a6..8a83ae82a 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -167,6 +167,7 @@ private: using SharedMediaType = Storage::SharedMediaType; using SliceKey = SparseIdsMergedSlice::Key; struct Streamed; + struct ShuffleData; struct Data { Data(AudioMsgId::Type type, SharedMediaType overview); Data(Data &&other); @@ -195,6 +196,7 @@ private: bool isPlaying = false; bool resumeOnCallEnd = false; std::unique_ptr<Streamed> streamed; + std::unique_ptr<ShuffleData> shuffleData; }; struct SeekingChanges { @@ -237,6 +239,11 @@ private: HistoryItem *itemByIndex(not_null<Data*> data, int index); void stopAndClear(not_null<Data*> data); + [[nodiscard]] MsgId computeCurrentUniversalId( + not_null<const Data*> data) const; + void validateShuffleData(not_null<Data*> data); + void setupShuffleData(not_null<Data*> data); + void handleStreamingUpdate( not_null<Data*> data, Streaming::Update &&update); From 433169626bf100a16d7a4ed1459762d8989db993 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Nov 2021 18:01:58 +0400 Subject: [PATCH 076/180] Fix restarting shuffled playlist. --- .../media/player/media_player_instance.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 2028f0201..ec06d79d9 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -796,8 +796,27 @@ void Instance::validateShuffleData(not_null<Data*> data) { raw->nonPlayedIds.clear(); raw->playedIds.clear(); raw->indexInPlayedIds = 0; - } else if (raw->allLoaded || raw->nextSliceLifetime) { + } else if (raw->nextSliceLifetime) { return; + } else if (raw->allLoaded) { + const auto universal = computeCurrentUniversalId(data); + if (!universal + || (raw->indexInPlayedIds < raw->playedIds.size() + ? (raw->playedIds[raw->indexInPlayedIds] == universal) + : ranges::contains(raw->nonPlayedIds, universal))) { + return; + } + // We started playing some track not from the tracks that are left. + // Start the whole playlist thing once again. + raw->playedIds.clear(); + raw->indexInPlayedIds = 0; + if (ranges::contains(raw->playlist, universal)) { + raw->nonPlayedIds = raw->playlist; + } else { + raw->allLoaded = false; + raw->playlist.clear(); + raw->nonPlayedIds.clear(); + } } if (raw->scheduled) { const auto count = data->playlistSlice From 7ef78ba6c977ca383da4991b987b3fdc6d46935e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Nov 2021 19:27:54 +0400 Subject: [PATCH 077/180] Fix contact status buttons display. --- .../SourceFiles/history/view/history_view_contact_status.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp index a55325f44..4d7932de1 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp @@ -392,6 +392,7 @@ void ContactStatus::setupState(not_null<PeerData*> peer) { peer->session().api().requestPeerSettings(peer); } + _bar.entity()->showState(State()); PeerState( peer ) | rpl::start_with_next([=](State state) { From 38367dc1c727885fb7161d9290d8ac9d6ef23ebe Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 23 Nov 2021 20:11:32 +0400 Subject: [PATCH 078/180] Allow repeat all in shuffle mode. --- .../media/player/media_player_instance.cpp | 163 +++++++++++++----- .../media/player/media_player_instance.h | 10 +- 2 files changed, 129 insertions(+), 44 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index ec06d79d9..a16b66e6f 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -45,6 +45,7 @@ constexpr auto kIdsLimit = 32; constexpr auto kIdsPreloadAfter = 28; constexpr auto kShufflePlaylistLimit = 10'000; +constexpr auto kRememberShuffledOrderItems = 16; constexpr auto kMinLengthForSavePosition = 20 * TimeId(60); // 20 minutes. @@ -123,10 +124,6 @@ Instance::Streamed::Streamed( Instance::Data::Data(AudioMsgId::Type type, SharedMediaType overview) : type(type) , overview(overview) { - if (type == AudioMsgId::Type::Song) { - repeat = Core::App().settings().playerRepeatModeValue(); - order = Core::App().settings().playerOrderModeValue(); - } } Instance::Data::Data(Data &&other) = default; @@ -140,12 +137,22 @@ Instance::Instance() handleSongUpdate(audioId); }); - _songData.repeat.changes( + repeatChanges( + &_songData ) | rpl::start_with_next([=](RepeatMode mode) { if (mode == RepeatMode::All) { refreshPlaylist(&_songData); } }, _lifetime); + orderChanges( + &_songData + ) | rpl::start_with_next([=](OrderMode mode) { + if (mode == OrderMode::Shuffle) { + validateShuffleData(&_songData); + } else { + _songData.shuffleData = nullptr; + } + }, _lifetime); using namespace rpl::mappers; rpl::combine( @@ -296,10 +303,8 @@ void Instance::playlistUpdated(not_null<Data*> data) { if (data->playlistSlice) { const auto fullId = data->current.contextId(); data->playlistIndex = data->playlistSlice->indexOf(fullId); - if (data->order.current() == OrderMode::Shuffle) { + if (order(data) == OrderMode::Shuffle) { validateShuffleData(data); - } else { - data->shuffleData = nullptr; } } else { data->playlistIndex = std::nullopt; @@ -422,8 +427,8 @@ void Instance::validateOtherPlaylist(not_null<Data*> data) { auto Instance::playlistOtherKey(not_null<const Data*> data) const -> std::optional<SliceKey> { - if (data->repeat.current() != RepeatMode::All - || data->order.current() == OrderMode::Shuffle + if (repeat(data) != RepeatMode::All + || order(data) == OrderMode::Shuffle || !data->playlistSlice || (data->playlistSlice->skippedBefore() != 0 && data->playlistSlice->skippedAfter() != 0) @@ -493,46 +498,52 @@ bool Instance::moveInPlaylist( return jumpByItem(data->history->owner().message(id)); }; - if (data->order.current() == OrderMode::Shuffle) { + if (order(data) == OrderMode::Shuffle) { const auto raw = data->shuffleData.get(); if (!raw || !raw->history) { return false; } + const auto universal = computeCurrentUniversalId(data); const auto byUniversal = [&](ShuffleData::UniversalMsgId id) { return (id < 0) ? jumpById({ ChannelId(), id + ServerMaxMsgId }) : jumpById({ raw->history->channelId(), id }); }; + if (universal && raw->indexInPlayedIds == raw->playedIds.size()) { + raw->playedIds.push_back(universal); + const auto i = ranges::find(raw->nonPlayedIds, universal); + if (i != end(raw->nonPlayedIds)) { + raw->nonPlayedIds.erase(i); + } + } + if (repeat(data) == RepeatMode::All) { + ensureShuffleMove(data, delta); + } + if (raw->nonPlayedIds.empty() + && raw->indexInPlayedIds + 1 == raw->playedIds.size()) { + raw->nonPlayedIds.push_back(raw->playedIds.back()); + raw->playedIds.pop_back(); + } + const auto shuffleCompleted = raw->nonPlayedIds.empty() + || (raw->nonPlayedIds.size() == 1 + && raw->nonPlayedIds.front() == universal); if (delta < 0) { return (raw->indexInPlayedIds > 0) && byUniversal(raw->playedIds[--raw->indexInPlayedIds]); } else if (raw->indexInPlayedIds + 1 < raw->playedIds.size()) { return byUniversal(raw->playedIds[++raw->indexInPlayedIds]); - } else { - if (raw->nonPlayedIds.empty()) { - return false; - } - if (const auto universal = computeCurrentUniversalId(data)) { - if (raw->nonPlayedIds.size() == 1 - && raw->nonPlayedIds.front() == universal) { - return false; - } - if (raw->indexInPlayedIds == raw->playedIds.size()) { - raw->playedIds.push_back(universal); - } - const auto i = ranges::find(raw->nonPlayedIds, universal); - if (i != end(raw->nonPlayedIds)) { - raw->nonPlayedIds.erase(i); - } - ++raw->indexInPlayedIds; - } - const auto index = base::RandomIndex(raw->nonPlayedIds.size()); - return byUniversal(raw->nonPlayedIds[index]); } + if (shuffleCompleted) { + return false; + } else if (raw->indexInPlayedIds < raw->playedIds.size()) { + ++raw->indexInPlayedIds; + } + const auto index = base::RandomIndex(raw->nonPlayedIds.size()); + return byUniversal(raw->nonPlayedIds[index]); } const auto newIndex = *data->playlistIndex - + (data->order.current() == OrderMode::Reverse ? -delta : delta); + + (order(data) == OrderMode::Reverse ? -delta : delta); const auto useIndex = (!data->playlistSlice || data->playlistSlice->skippedAfter() != 0 || data->playlistSlice->skippedBefore() != 0 @@ -542,7 +553,7 @@ bool Instance::moveInPlaylist( % int(data->playlistSlice->size())); if (const auto item = itemByIndex(data, useIndex)) { return jumpByItem(item); - } else if (data->repeat.current() == RepeatMode::All + } else if (repeat(data) == RepeatMode::All && data->playlistOtherSlice && data->playlistOtherSlice->size() > 0) { const auto &other = *data->playlistOtherSlice; @@ -555,6 +566,48 @@ bool Instance::moveInPlaylist( return false; } +void Instance::ensureShuffleMove(not_null<Data*> data, int delta) { + const auto raw = data->shuffleData.get(); + if (delta < 0) { + if (raw->indexInPlayedIds > 0) { + return; + } else if (raw->nonPlayedIds.size() < 2) { + const auto freeUp = std::max( + int(raw->playedIds.size() / 2), + int(raw->playlist.size()) - kRememberShuffledOrderItems); + const auto till = end(raw->playedIds); + const auto from = end(raw->playedIds) - freeUp; + raw->nonPlayedIds.insert(end(raw->nonPlayedIds), from, till); + raw->playedIds.erase(from, till); + } + if (raw->nonPlayedIds.empty()) { + return; + } + const auto index = base::RandomIndex(raw->nonPlayedIds.size()); + raw->playedIds.insert( + begin(raw->playedIds), + raw->nonPlayedIds[index]); + raw->nonPlayedIds.erase(begin(raw->nonPlayedIds) + index); + ++raw->indexInPlayedIds; + if (raw->nonPlayedIds.empty() && raw->playedIds.size() > 1) { + raw->nonPlayedIds.push_back(raw->playedIds.back()); + raw->playedIds.pop_back(); + } + return; + } else if (raw->indexInPlayedIds + 1 < raw->playedIds.size()) { + return; + } else if (raw->nonPlayedIds.size() < 2) { + const auto freeUp = std::max( + int(raw->playedIds.size() / 2), + int(raw->playlist.size()) - kRememberShuffledOrderItems); + const auto from = begin(raw->playedIds); + const auto till = begin(raw->playedIds) + freeUp; + raw->nonPlayedIds.insert(end(raw->nonPlayedIds), from, till); + raw->playedIds.erase(from, till); + raw->indexInPlayedIds -= freeUp; + } +} + MsgId Instance::computeCurrentUniversalId(not_null<const Data*> data) const { const auto raw = data->shuffleData.get(); if (!raw) { @@ -577,13 +630,13 @@ bool Instance::previousAvailable(AudioMsgId::Type type) const { if (!data->playlistIndex || !data->playlistSlice) { return false; - } else if (data->repeat.current() == RepeatMode::All) { + } else if (repeat(data) == RepeatMode::All) { return true; - } else if (data->order.current() == OrderMode::Shuffle) { + } else if (order(data) == OrderMode::Shuffle) { const auto raw = data->shuffleData.get(); return raw && (raw->indexInPlayedIds > 0); } - return (data->order.current() == OrderMode::Reverse) + return (order(data) == OrderMode::Reverse) ? (*data->playlistIndex + 1 < data->playlistSlice->size()) : (*data->playlistIndex > 0); } @@ -594,9 +647,9 @@ bool Instance::nextAvailable(AudioMsgId::Type type) const { if (!data->playlistIndex || !data->playlistSlice) { return false; - } else if (data->repeat.current() == RepeatMode::All) { + } else if (repeat(data) == RepeatMode::All) { return true; - } else if (data->order.current() == OrderMode::Shuffle) { + } else if (order(data) == OrderMode::Shuffle) { const auto raw = data->shuffleData.get(); const auto universal = computeCurrentUniversalId(data); return raw @@ -605,7 +658,7 @@ bool Instance::nextAvailable(AudioMsgId::Type type) const { || (!raw->nonPlayedIds.empty() && raw->nonPlayedIds.front() != universal)); } - return (data->order.current() == OrderMode::Reverse) + return (order(data) == OrderMode::Reverse) ? (*data->playlistIndex > 0) : (*data->playlistIndex + 1 < data->playlistSlice->size()); } @@ -617,8 +670,8 @@ rpl::producer<> Media::Player::Instance::playlistChanges( return rpl::merge( data->playlistChanges.events(), - data->order.changes() | rpl::to_empty, - data->repeat.changes() | rpl::to_empty); + orderChanges(data) | rpl::to_empty, + repeatChanges(data) | rpl::to_empty); } rpl::producer<> Media::Player::Instance::stops(AudioMsgId::Type type) const { @@ -1044,6 +1097,32 @@ void Instance::emitUpdate(AudioMsgId::Type type) { emitUpdate(type, [](const AudioMsgId &playing) { return true; }); } +RepeatMode Instance::repeat(not_null<const Data*> data) const { + return (data->type == AudioMsgId::Type::Song) + ? Core::App().settings().playerRepeatMode() + : RepeatMode::None; +} + +rpl::producer<RepeatMode> Instance::repeatChanges( + not_null<const Data*> data) const { + return (data->type == AudioMsgId::Type::Song) + ? Core::App().settings().playerRepeatModeChanges() + : rpl::never<RepeatMode>(); +} + +OrderMode Instance::order(not_null<const Data*> data) const { + return (data->type == AudioMsgId::Type::Song) + ? Core::App().settings().playerOrderMode() + : OrderMode::Default; +} + +rpl::producer<OrderMode> Instance::orderChanges( + not_null<const Data*> data) const { + return (data->type == AudioMsgId::Type::Song) + ? Core::App().settings().playerOrderModeChanges() + : rpl::never<OrderMode>(); +} + TrackState Instance::getState(AudioMsgId::Type type) const { if (const auto data = getData(type)) { if (data->streamed) { @@ -1092,7 +1171,7 @@ void Instance::emitUpdate(AudioMsgId::Type type, CheckCallback check) { auto finished = false; _updatedNotifier.fire_copy({state}); if (data->isPlaying && state.state == State::StoppedAtEnd) { - if (data->repeat.current() == RepeatMode::One) { + if (repeat(data) == RepeatMode::One) { play(data->current); } else if (!moveInPlaylist(data, 1, true)) { finished = true; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h index 8a83ae82a..5155ac69b 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.h +++ b/Telegram/SourceFiles/media/player/media_player_instance.h @@ -191,8 +191,6 @@ private: History *history = nullptr; History *migrated = nullptr; Main::Session *session = nullptr; - rpl::variable<RepeatMode> repeat = RepeatMode::None; - rpl::variable<OrderMode> order = OrderMode::Default; bool isPlaying = false; bool resumeOnCallEnd = false; std::unique_ptr<Streamed> streamed; @@ -243,6 +241,7 @@ private: not_null<const Data*> data) const; void validateShuffleData(not_null<Data*> data); void setupShuffleData(not_null<Data*> data); + void ensureShuffleMove(not_null<Data*> data, int delta); void handleStreamingUpdate( not_null<Data*> data, @@ -256,6 +255,13 @@ private: template <typename CheckCallback> void emitUpdate(AudioMsgId::Type type, CheckCallback check); + [[nodiscard]] RepeatMode repeat(not_null<const Data*> data) const; + [[nodiscard]] rpl::producer<RepeatMode> repeatChanges( + not_null<const Data*> data) const; + [[nodiscard]] OrderMode order(not_null<const Data*> data) const; + [[nodiscard]] rpl::producer<OrderMode> orderChanges( + not_null<const Data*> data) const; + Data *getData(AudioMsgId::Type type) { if (type == AudioMsgId::Type::Song) { return &_songData; From 96c86b3e49c600256c3a7b1f3c88750931f7d1b9 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 24 Nov 2021 12:24:26 +0400 Subject: [PATCH 079/180] Update API scheme on layer 135. --- Telegram/Resources/tl/api.tl | 14 ++-- .../SourceFiles/api/api_confirm_phone.cpp | 5 +- Telegram/SourceFiles/apiwrap.cpp | 2 +- .../SourceFiles/boxes/change_phone_box.cpp | 6 +- Telegram/SourceFiles/intro/intro_phone.cpp | 2 +- Telegram/SourceFiles/intro/intro_step.cpp | 24 +++--- .../passport/passport_form_controller.cpp | 82 +++++++++---------- 7 files changed, 70 insertions(+), 65 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 21aed824a..4f63ffd6f 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -142,7 +142,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto; chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto; messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message; -message#85d6cbe2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message; +message#85d6cbe2 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true id:int from_id:flags.8?Peer peer_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> ttl_period:flags.25?int = Message; messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message; messageMediaEmpty#3ded6320 = MessageMedia; @@ -208,7 +208,7 @@ geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radiu auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode; -auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization; +auth.authorization#33fb7bb8 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int user:User = auth.Authorization; auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization; auth.exportedAuthorization#b434e2b8 id:long bytes:bytes = auth.ExportedAuthorization; @@ -631,7 +631,7 @@ channelMessagesFilterEmpty#94d42ee7 = ChannelMessagesFilter; channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:Vector<MessageRange> = ChannelMessagesFilter; channelParticipant#c00c07c0 user_id:long date:int = ChannelParticipant; -channelParticipantSelf#35a8bfa7 flags:# via_invite:flags.0?true user_id:long inviter_id:long date:int = ChannelParticipant; +channelParticipantSelf#35a8bfa7 flags:# via_request:flags.0?true user_id:long inviter_id:long date:int = ChannelParticipant; channelParticipantCreator#2fe601d3 flags:# user_id:long admin_rights:ChatAdminRights rank:flags.0?string = ChannelParticipant; channelParticipantAdmin#34c3bb53 flags:# can_edit:flags.0?true self:flags.1?true user_id:long inviter_id:flags.1?long promoted_by:long date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant; channelParticipantBanned#6df8014e flags:# left:flags.0?true peer:Peer kicked_by:long date:int banned_rights:ChatBannedRights = ChannelParticipant; @@ -688,11 +688,13 @@ messageFwdHeader#5f777dce flags:# imported:flags.7?true from_id:flags.0?Peer fro auth.codeTypeSms#72a3158c = auth.CodeType; auth.codeTypeCall#741cd3e3 = auth.CodeType; auth.codeTypeFlashCall#226ccefb = auth.CodeType; +auth.codeTypeMissedCall#d61ad6ee = auth.CodeType; auth.sentCodeTypeApp#3dbb5986 length:int = auth.SentCodeType; auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType; auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType; auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType; +auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeType; messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer; @@ -1091,7 +1093,7 @@ inputWallPaperNoFile#967a462e id:long = InputWallPaper; account.wallPapersNotModified#1c199183 = account.WallPapers; account.wallPapers#cdc3858c hash:long wallpapers:Vector<WallPaper> = account.WallPapers; -codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings; +codeSettings#8a6469c2 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true allow_missed_call:flags.5?true logout_tokens:flags.6?Vector<bytes> = CodeSettings; wallPaperSettings#1dc1bca4 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int second_background_color:flags.4?int third_background_color:flags.5?int fourth_background_color:flags.6?int intensity:flags.3?int rotation:flags.4?int = WallPaperSettings; @@ -1298,6 +1300,8 @@ users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Vector<User> = messages.PeerSettings; +auth.loggedOut#8b591226 flags:# future_auth_token:flags.0?bytes future_auth_expires:flags.0?int = auth.LoggedOut; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -1311,7 +1315,7 @@ invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode; auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization; auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; -auth.logOut#5717da40 = Bool; +auth.logOut#3e72ba19 = auth.LoggedOut; auth.resetAuthorizations#9fab0d1a = Bool; auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; auth.importAuthorization#a57a7dad id:long bytes:bytes = auth.Authorization; diff --git a/Telegram/SourceFiles/api/api_confirm_phone.cpp b/Telegram/SourceFiles/api/api_confirm_phone.cpp index cfe13eb07..559f16136 100644 --- a/Telegram/SourceFiles/api/api_confirm_phone.cpp +++ b/Telegram/SourceFiles/api/api_confirm_phone.cpp @@ -30,7 +30,7 @@ void ConfirmPhone::resolve( } _sendRequestId = _api.request(MTPaccount_SendConfirmPhoneCode( MTP_string(hash), - MTP_codeSettings(MTP_flags(0)) + MTP_codeSettings(MTP_flags(0), MTP_vector<MTPbytes>()) )).done([=](const MTPauth_SentCode &result) { _sendRequestId = 0; @@ -46,6 +46,9 @@ void ConfirmPhone::resolve( }, [&](const MTPDauth_sentCodeTypeFlashCall &data) { LOG(("Error: should not be flashcall!")); return 0; + }, [&](const MTPDauth_sentCodeTypeMissedCall &data) { + LOG(("Error: should not be missedcall!")); + return 0; }); const auto phoneHash = qs(data.vphone_code_hash()); const auto timeout = [&]() -> std::optional<int> { diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 6c7cd9cab..0f2af9379 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1657,7 +1657,7 @@ void ApiWrap::requestSelfParticipant(not_null<ChannelData*> channel) { finalize( data.vinviter_id().v, data.vdate().v, - data.is_via_invite()); + data.is_via_request()); }, [&](const MTPDchannelParticipantCreator &) { if (channel->mgInfo) { channel->mgInfo->creator = _session->user(); diff --git a/Telegram/SourceFiles/boxes/change_phone_box.cpp b/Telegram/SourceFiles/boxes/change_phone_box.cpp index e35331ecb..dc8194cbe 100644 --- a/Telegram/SourceFiles/boxes/change_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/change_phone_box.cpp @@ -191,7 +191,7 @@ void ChangePhoneBox::EnterPhone::submit() { const auto phoneNumber = _phone->getLastText().trimmed(); _requestId = _api.request(MTPaccount_SendChangePhoneCode( MTP_string(phoneNumber), - MTP_codeSettings(MTP_flags(0)) + MTP_codeSettings(MTP_flags(0), MTP_vector<MTPbytes>()) )).done([=](const MTPauth_SentCode &result) { _requestId = 0; sendPhoneDone(result, phoneNumber); @@ -225,6 +225,10 @@ void ChangePhoneBox::EnterPhone::sendPhoneDone( LOG(("Error: should not be flashcall!")); showError(Lang::Hard::ServerError()); return false; + }, [&](const MTPDauth_sentCodeTypeMissedCall &data) { + LOG(("Error: should not be missedcall!")); + showError(Lang::Hard::ServerError()); + return false; }); if (!hasLength) { return; diff --git a/Telegram/SourceFiles/intro/intro_phone.cpp b/Telegram/SourceFiles/intro/intro_phone.cpp index b85b6d592..8237f2e9b 100644 --- a/Telegram/SourceFiles/intro/intro_phone.cpp +++ b/Telegram/SourceFiles/intro/intro_phone.cpp @@ -175,7 +175,7 @@ void PhoneWidget::submit() { MTP_string(_sentPhone), MTP_int(ApiId), MTP_string(ApiHash), - MTP_codeSettings(MTP_flags(0)) + MTP_codeSettings(MTP_flags(0), MTP_vector<MTPbytes>()) )).done([=](const MTPauth_SentCode &result) { phoneSubmitDone(result); }).fail([=](const MTP::Error &error) { diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp index c1b559b6c..6634ec32f 100644 --- a/Telegram/SourceFiles/intro/intro_step.cpp +++ b/Telegram/SourceFiles/intro/intro_step.cpp @@ -309,22 +309,20 @@ bool Step::paintAnimated(Painter &p, QRect clip) { } void Step::fillSentCodeData(const MTPDauth_sentCode &data) { - const auto &type = data.vtype(); - switch (type.type()) { - case mtpc_auth_sentCodeTypeApp: { + data.vtype().match([&](const MTPDauth_sentCodeTypeApp &data) { getData()->codeByTelegram = true; - getData()->codeLength = type.c_auth_sentCodeTypeApp().vlength().v; - } break; - case mtpc_auth_sentCodeTypeSms: { + getData()->codeLength = data.vlength().v; + }, [&](const MTPDauth_sentCodeTypeSms &data) { getData()->codeByTelegram = false; - getData()->codeLength = type.c_auth_sentCodeTypeSms().vlength().v; - } break; - case mtpc_auth_sentCodeTypeCall: { + getData()->codeLength = data.vlength().v; + }, [&](const MTPDauth_sentCodeTypeCall &data) { getData()->codeByTelegram = false; - getData()->codeLength = type.c_auth_sentCodeTypeCall().vlength().v; - } break; - case mtpc_auth_sentCodeTypeFlashCall: LOG(("Error: should not be flashcall!")); break; - } + getData()->codeLength = data.vlength().v; + }, [&](const MTPDauth_sentCodeTypeFlashCall &) { + LOG(("Error: should not be flashcall!")); + }, [&](const MTPDauth_sentCodeTypeMissedCall &data) { + LOG(("Error: should not be missedcall!")); + }); } void Step::showDescription() { diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 7680cd0ec..f2bc757f5 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -2161,54 +2161,50 @@ QString FormController::getPlainTextFromValue( void FormController::startPhoneVerification(not_null<Value*> value) { value->verification.requestId = _api.request(MTPaccount_SendVerifyPhoneCode( MTP_string(getPhoneFromValue(value)), - MTP_codeSettings(MTP_flags(0)) + MTP_codeSettings(MTP_flags(0), MTP_vector<MTPbytes>()) )).done([=](const MTPauth_SentCode &result) { - Expects(result.type() == mtpc_auth_sentCode); - - value->verification.requestId = 0; - - const auto &data = result.c_auth_sentCode(); - value->verification.phoneCodeHash = qs(data.vphone_code_hash()); - switch (data.vtype().type()) { - case mtpc_auth_sentCodeTypeApp: - LOG(("API Error: sentCodeTypeApp not expected " - "in FormController::startPhoneVerification.")); - return; - case mtpc_auth_sentCodeTypeFlashCall: - LOG(("API Error: sentCodeTypeFlashCall not expected " - "in FormController::startPhoneVerification.")); - return; - case mtpc_auth_sentCodeTypeCall: { - const auto &type = data.vtype().c_auth_sentCodeTypeCall(); - value->verification.codeLength = (type.vlength().v > 0) - ? type.vlength().v - : -1; - value->verification.call = std::make_unique<Ui::SentCodeCall>( - [=] { requestPhoneCall(value); }, - [=] { _verificationUpdate.fire_copy(value); }); - value->verification.call->setStatus( - { Ui::SentCodeCall::State::Called, 0 }); - if (data.vnext_type()) { - LOG(("API Error: next_type is not supported for calls.")); - } - } break; - case mtpc_auth_sentCodeTypeSms: { - const auto &type = data.vtype().c_auth_sentCodeTypeSms(); - value->verification.codeLength = (type.vlength().v > 0) - ? type.vlength().v - : -1; + result.match([&](const MTPDauth_sentCode &data) { const auto next = data.vnext_type(); - if (next && next->type() == mtpc_auth_codeTypeCall) { + const auto timeout = data.vtimeout(); + value->verification.requestId = 0; + value->verification.phoneCodeHash = qs(data.vphone_code_hash()); + data.vtype().match([&](const MTPDauth_sentCodeTypeApp &) { + LOG(("API Error: sentCodeTypeApp not expected " + "in FormController::startPhoneVerification.")); + }, [&](const MTPDauth_sentCodeTypeFlashCall &) { + LOG(("API Error: sentCodeTypeFlashCall not expected " + "in FormController::startPhoneVerification.")); + }, [&](const MTPDauth_sentCodeTypeMissedCall &data) { + LOG(("API Error: sentCodeTypeMissedCall not expected " + "in FormController::startPhoneVerification.")); + }, [&](const MTPDauth_sentCodeTypeCall &data) { + value->verification.codeLength = (data.vlength().v > 0) + ? data.vlength().v + : -1; value->verification.call = std::make_unique<Ui::SentCodeCall>( [=] { requestPhoneCall(value); }, [=] { _verificationUpdate.fire_copy(value); }); - value->verification.call->setStatus({ - Ui::SentCodeCall::State::Waiting, - data.vtimeout().value_or(60) }); - } - } break; - } - _verificationNeeded.fire_copy(value); + value->verification.call->setStatus( + { Ui::SentCodeCall::State::Called, 0 }); + if (next) { + LOG(("API Error: next_type is not supported for calls.")); + } + }, [&](const MTPDauth_sentCodeTypeSms &data) { + value->verification.codeLength = (data.vlength().v > 0) + ? data.vlength().v + : -1; + if (next && next->type() == mtpc_auth_codeTypeCall) { + value->verification.call = std::make_unique<Ui::SentCodeCall>( + [=] { requestPhoneCall(value); }, + [=] { _verificationUpdate.fire_copy(value); }); + value->verification.call->setStatus({ + Ui::SentCodeCall::State::Waiting, + timeout.value_or(60), + }); + } + }); + _verificationNeeded.fire_copy(value); + }); }).fail([=](const MTP::Error &error) { value->verification.requestId = 0; valueSaveShowError(value, error); From aef37be3d0cd1b11511c8871ae2fedfcf69e0b29 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 24 Nov 2021 12:31:41 +0400 Subject: [PATCH 080/180] Move box divider colors to the palette. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 1111718ea..f4a32c34f 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 1111718ea3d78c1abeb3d94c11b36e540bc94c4a +Subproject commit f4a32c34f830b498a7348a94f171c7557a8252c3 From ca61b80fe52e7097a1a59964f97fa934639d1f6b Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 24 Nov 2021 16:23:10 +0400 Subject: [PATCH 081/180] Update audio player icons. --- .../Resources/icons/player/panel_close.png | Bin 0 -> 284 bytes .../Resources/icons/player/panel_close@2x.png | Bin 0 -> 410 bytes .../Resources/icons/player/panel_close@3x.png | Bin 0 -> 593 bytes .../icons/player/player_backward.png | Bin 0 -> 314 bytes .../icons/player/player_backward@2x.png | Bin 0 -> 524 bytes .../icons/player/player_backward@3x.png | Bin 0 -> 736 bytes .../Resources/icons/player/player_close.png | Bin 133 -> 0 bytes .../icons/player/player_close@2x.png | Bin 197 -> 0 bytes .../icons/player/player_close@3x.png | Bin 342 -> 0 bytes .../Resources/icons/player/player_forward.png | Bin 0 -> 297 bytes .../icons/player/player_forward@2x.png | Bin 0 -> 510 bytes .../icons/player/player_forward@3x.png | Bin 0 -> 775 bytes .../icons/player/player_mini_full.png | Bin 0 -> 540 bytes .../icons/player/player_mini_full@2x.png | Bin 0 -> 995 bytes .../icons/player/player_mini_full@3x.png | Bin 0 -> 1506 bytes .../icons/player/player_mini_half.png | Bin 0 -> 412 bytes .../icons/player/player_mini_half@2x.png | Bin 0 -> 665 bytes .../icons/player/player_mini_half@3x.png | Bin 0 -> 951 bytes .../icons/player/player_mini_off.png | Bin 0 -> 306 bytes .../icons/player/player_mini_off@2x.png | Bin 0 -> 450 bytes .../icons/player/player_mini_off@3x.png | Bin 0 -> 647 bytes .../Resources/icons/player/player_next.png | Bin 219 -> 0 bytes .../Resources/icons/player/player_next@2x.png | Bin 319 -> 0 bytes .../Resources/icons/player/player_next@3x.png | Bin 554 -> 0 bytes .../Resources/icons/player/player_order.png | Bin 0 -> 314 bytes .../icons/player/player_order@2x.png | Bin 0 -> 527 bytes .../icons/player/player_order@3x.png | Bin 0 -> 772 bytes .../icons/player/player_panel_next.png | Bin 188 -> 0 bytes .../icons/player/player_panel_next@2x.png | Bin 297 -> 0 bytes .../icons/player/player_panel_next@3x.png | Bin 629 -> 0 bytes .../icons/player/player_panel_pin.png | Bin 349 -> 0 bytes .../icons/player/player_panel_pin@2x.png | Bin 547 -> 0 bytes .../icons/player/player_panel_pin@3x.png | Bin 825 -> 0 bytes .../Resources/icons/player/player_pause.png | Bin 215 -> 229 bytes .../icons/player/player_pause@2x.png | Bin 400 -> 306 bytes .../icons/player/player_pause@3x.png | Bin 593 -> 422 bytes .../Resources/icons/player/player_play.png | Bin 362 -> 298 bytes .../Resources/icons/player/player_play@2x.png | Bin 659 -> 437 bytes .../Resources/icons/player/player_play@3x.png | Bin 1318 -> 609 bytes .../Resources/icons/player/player_repeat.png | Bin 211 -> 396 bytes .../icons/player/player_repeat@2x.png | Bin 235 -> 606 bytes .../icons/player/player_repeat@3x.png | Bin 303 -> 1011 bytes .../icons/player/player_repeat_one.png | Bin 262 -> 0 bytes .../icons/player/player_repeat_one@2x.png | Bin 304 -> 0 bytes .../icons/player/player_repeat_one@3x.png | Bin 377 -> 0 bytes .../icons/player/player_repeat_reverse.png | Bin 246 -> 0 bytes .../icons/player/player_repeat_reverse@2x.png | Bin 301 -> 0 bytes .../icons/player/player_repeat_reverse@3x.png | Bin 371 -> 0 bytes .../icons/player/player_repeat_shuffle.png | Bin 269 -> 0 bytes .../icons/player/player_repeat_shuffle@2x.png | Bin 290 -> 0 bytes .../icons/player/player_repeat_shuffle@3x.png | Bin 387 -> 0 bytes .../icons/player/player_repeat_single.png | Bin 0 -> 449 bytes .../icons/player/player_repeat_single@2x.png | Bin 0 -> 731 bytes .../icons/player/player_repeat_single@3x.png | Bin 0 -> 1233 bytes .../Resources/icons/player/player_shuffle.png | Bin 346 -> 514 bytes .../icons/player/player_shuffle@2x.png | Bin 537 -> 924 bytes .../icons/player/player_shuffle@3x.png | Bin 761 -> 1419 bytes .../Resources/icons/player/player_volume0.png | Bin 129 -> 0 bytes .../icons/player/player_volume0@2x.png | Bin 167 -> 0 bytes .../icons/player/player_volume0@3x.png | Bin 362 -> 0 bytes .../Resources/icons/player/player_volume1.png | Bin 139 -> 0 bytes .../icons/player/player_volume1@2x.png | Bin 217 -> 0 bytes .../icons/player/player_volume1@3x.png | Bin 478 -> 0 bytes .../Resources/icons/player/player_volume2.png | Bin 154 -> 0 bytes .../icons/player/player_volume2@2x.png | Bin 226 -> 0 bytes .../icons/player/player_volume2@3x.png | Bin 646 -> 0 bytes .../Resources/icons/player/player_volume3.png | Bin 157 -> 0 bytes .../icons/player/player_volume3@2x.png | Bin 242 -> 0 bytes .../icons/player/player_volume3@3x.png | Bin 875 -> 0 bytes .../media/player/media_player.style | 134 ++++++------- .../media/player/media_player_instance.cpp | 8 +- .../player/media_player_repeat_controls.cpp | 4 +- .../media/player/media_player_widget.cpp | 187 ++++++++++-------- .../media/player/media_player_widget.h | 15 +- 74 files changed, 185 insertions(+), 163 deletions(-) create mode 100644 Telegram/Resources/icons/player/panel_close.png create mode 100644 Telegram/Resources/icons/player/panel_close@2x.png create mode 100644 Telegram/Resources/icons/player/panel_close@3x.png create mode 100644 Telegram/Resources/icons/player/player_backward.png create mode 100644 Telegram/Resources/icons/player/player_backward@2x.png create mode 100644 Telegram/Resources/icons/player/player_backward@3x.png delete mode 100644 Telegram/Resources/icons/player/player_close.png delete mode 100644 Telegram/Resources/icons/player/player_close@2x.png delete mode 100644 Telegram/Resources/icons/player/player_close@3x.png create mode 100644 Telegram/Resources/icons/player/player_forward.png create mode 100644 Telegram/Resources/icons/player/player_forward@2x.png create mode 100644 Telegram/Resources/icons/player/player_forward@3x.png create mode 100644 Telegram/Resources/icons/player/player_mini_full.png create mode 100644 Telegram/Resources/icons/player/player_mini_full@2x.png create mode 100644 Telegram/Resources/icons/player/player_mini_full@3x.png create mode 100644 Telegram/Resources/icons/player/player_mini_half.png create mode 100644 Telegram/Resources/icons/player/player_mini_half@2x.png create mode 100644 Telegram/Resources/icons/player/player_mini_half@3x.png create mode 100644 Telegram/Resources/icons/player/player_mini_off.png create mode 100644 Telegram/Resources/icons/player/player_mini_off@2x.png create mode 100644 Telegram/Resources/icons/player/player_mini_off@3x.png delete mode 100644 Telegram/Resources/icons/player/player_next.png delete mode 100644 Telegram/Resources/icons/player/player_next@2x.png delete mode 100644 Telegram/Resources/icons/player/player_next@3x.png create mode 100644 Telegram/Resources/icons/player/player_order.png create mode 100644 Telegram/Resources/icons/player/player_order@2x.png create mode 100644 Telegram/Resources/icons/player/player_order@3x.png delete mode 100644 Telegram/Resources/icons/player/player_panel_next.png delete mode 100644 Telegram/Resources/icons/player/player_panel_next@2x.png delete mode 100644 Telegram/Resources/icons/player/player_panel_next@3x.png delete mode 100644 Telegram/Resources/icons/player/player_panel_pin.png delete mode 100644 Telegram/Resources/icons/player/player_panel_pin@2x.png delete mode 100644 Telegram/Resources/icons/player/player_panel_pin@3x.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_one.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_one@2x.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_one@3x.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_reverse.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_reverse@2x.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_reverse@3x.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_shuffle.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_shuffle@2x.png delete mode 100644 Telegram/Resources/icons/player/player_repeat_shuffle@3x.png create mode 100644 Telegram/Resources/icons/player/player_repeat_single.png create mode 100644 Telegram/Resources/icons/player/player_repeat_single@2x.png create mode 100644 Telegram/Resources/icons/player/player_repeat_single@3x.png delete mode 100644 Telegram/Resources/icons/player/player_volume0.png delete mode 100644 Telegram/Resources/icons/player/player_volume0@2x.png delete mode 100644 Telegram/Resources/icons/player/player_volume0@3x.png delete mode 100644 Telegram/Resources/icons/player/player_volume1.png delete mode 100644 Telegram/Resources/icons/player/player_volume1@2x.png delete mode 100644 Telegram/Resources/icons/player/player_volume1@3x.png delete mode 100644 Telegram/Resources/icons/player/player_volume2.png delete mode 100644 Telegram/Resources/icons/player/player_volume2@2x.png delete mode 100644 Telegram/Resources/icons/player/player_volume2@3x.png delete mode 100644 Telegram/Resources/icons/player/player_volume3.png delete mode 100644 Telegram/Resources/icons/player/player_volume3@2x.png delete mode 100644 Telegram/Resources/icons/player/player_volume3@3x.png diff --git a/Telegram/Resources/icons/player/panel_close.png b/Telegram/Resources/icons/player/panel_close.png new file mode 100644 index 0000000000000000000000000000000000000000..2e333de4293b357bccf4e7eb51952f866bf8f627 GIT binary patch literal 284 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUwRyTYhFA!` zot(&ZK!Kw*S)8M(??&(L{@)1#No<BK2Q&89_}}+lyR>9qe2&8NJ5kz8&s=txv?#&P zl4q&XrRw!tUzrF@5@DSce@x~#$Cm2_jU}_czTMJu!y$fi($<Q+f~B8+K76|FOom?b z<A)jTJMZ-`i|ydO^VaIkufuXFH}p5{ZVLW>lP!;7%EZ~X@0{KUa+#;ApUXO@geCyk C9$s(& literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/panel_close@2x.png b/Telegram/Resources/icons/player/panel_close@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1a70b887fac6777a6e073a692b4cf325e92f4e0e GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GuF#^UMX7!twx zHtaN4i-Ex8zt;Kpe*J%w&ylx5<IxG9WuhgDdpV3dJukD%vhk`eV42b&6u{*5U?R)0 z!awVhXQ|y|I53sf>inV!Ki+TZtDNVp$-{2Ypv_&<HtE2dHiZKfR=XIV);V+3&(C}D z@(J6KD^8m&BPBz0X0}8od8>+WGwQCgJ3g@_>Ysh}?&*i7AB|I;m;dyq?)lYECaC*= z-N(tF;HTWZZQcy+Q{g{<ysdwAtK!wFukBYR9XiSDlVG#0ca3z2Ubk-X*&><5<x@iw zKQ2t+NSG=iqv>`?!j=E?uII;J%Vq3Vl*!lW5`T8hdfnZ>`p2%@e%hY-dNtH14xB8U Ze;5Mhq}BI_drbp{i>Irf%Q~loCIDm?kiY-{ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/panel_close@3x.png b/Telegram/Resources/icons/player/panel_close@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac24a1c653ecc813d6f15c6d4a68ec3de04e301 GIT binary patch literal 593 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}w%gOiF(iZa z?aYG{n+<qeH}7WGyW%7+;-I_b$W(3dT?-hZ6lZ;X@KNR9iAlZ8HF|{|`~Lhd=8^nV z@qmF*02A#nY_w1Pwrkz&Wtyjp=B1}<EBD_Ec(m&Eu2kD!`}Dh<E~Q?bFlW}9zhYVY z>WbcIFWjnjCP9L&&`fC3pQVbvg*nUq-@LW0_M>Bd>P@?4i}Fld+Y;VurxmrO-S%?5 z`)Q5D#**zVo}#nPPVSnO9-(Kv^tf31-6@~8Y&`nx$e}Hjciu5|Gj&=&E_|%8*zxG` z)El!pGp9#xGxeVLdH1q%?oU6Soq4tW-@n^RU%nmqIAPVX$!GUoziIgV*o0Hr8S!=7 zjF%dTSlm1}y=$^=>8kq|*5ADQdi~GPCrhj@x6a$V^-btKzM%YV7S+c$hlw=s%|0%B zp;G8r;e<uq_M)}Bq9+`hx=sDE$!4F|YSZja<OLM!v`?~QTFlgWWTu<)HVv_?J4IW) zioGnaFJ16%R{wpy-v_?9_3tpXSFn4xJ*R)i{-60v<PVx2o+@x<)v9HgQpYda>dU$3 lYm_l5ax^etpaY$pHjEDy>RG<r$;t&qr>Co*%Q~loCIFy7@A3cu literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_backward.png b/Telegram/Resources/icons/player/player_backward.png new file mode 100644 index 0000000000000000000000000000000000000000..d2cac60d1b643a37da2fbc20fae2645b431f8df1 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUE%9`546zV= zJIRpih=PdAVZDfz-#;>iZIe3~JzM$4O^p-l));U!?-skwk|fw>{#pH`f8S%f>$eYl z{;iz7wpCzj*pV3rqPUWMyflw>un0$ZC$2qfx!*3mWrj|BV#RxJ&BaUOt<#%61hoi6 zWoNu>I$0#qR-CskBJ+u&T)=<vKT1Aj?@z5x-E{k?{Opr?*MIyy_xT8eaD;u!lpsyh h_8Dj1tzEyh(cGE;UX-!ygHn*YJzf1=);T3K0RStcX43!w literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_backward@2x.png b/Telegram/Resources/icons/player/player_backward@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e7afc81f34702e485843107f33004ba53377d817 GIT binary patch literal 524 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GuFR_5vA7!twx zcADb4W(SEDZZXFR5hfKo7?@1Dv<p=G91rn+Sg_GUgma}v?QHi;IdvM=cU}K=q>1mF z>8D;&F3-Pj2V-7?^aC~^_23$tj<EUdZJ%w#`Ft<u+%C&%HCTRVe!f-T<!x_&|6ld` zS4cltplaUr(!2BAYSz83+Tt){vDfpy_rGiJ*I#<WoYZ3AvdEIRDM7}4lABMeb<4!c zzl9QQmrZ0ZpJ)_tvu|9}^SEN(^UEgl9XOP$U-e%$iJkT|dX}xgp%>~Crn&~&%s=04 z@<{N|iFv;sZ_<fhf4x-dqe$Wefv%}hYc@aHTNo?;G014z(jQh26XJJw%&+6U^Djhp z#i~{Lzo*P$OH8?!(X(d*N3uv$$He_6hXkS*XHHT%)hd|S<0Mch;(BT=b7IHZiy}8I z1dnO32zD<%YQU4S@=8L-k&G^xeF7jc<>SFEirZdoox|~D<&B6<I>OS2zADC_JTcGa i87PFn0o6EL`Ui7CpDovM3zv<cnDBJ<b6Mw<&;$UkHpu<} literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_backward@3x.png b/Telegram/Resources/icons/player/player_backward@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e83867321c4d6ce17e74f40861ea55ada1dc86da GIT binary patch literal 736 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbHUhz~trW;uw;_ z`ZjWJ7n7kx$#(}9ZSQDq!7Fjz(p$p?-n?B~!ybQ7T%d+I-oe{7JDQtS`(XSIZuOQ8 zH)F3Wa^5i%`}B7E%=~?ls<TsqHS1n5$Sz<mY2drSXyw3;#QE@F=Eo+jl~HT?+7G)e zF0_&3Z+CXm<C(f)M`L31!3Q5JYW~-~`(C~Js#lPRv+By6v@;ndRi2aXufF;!H<Rb< zwly0WAAgK^HSyr*pFQda0zR7d-u+rzZ#2`xuu-9l&Dk~7c)tJg%bRcND7fwAS$ZU3 z;nFoKn{U3Uu?tUqvC*$*;TNlIMmuBjcE-H&Vw%aY*FjgT`+&q>qlTjLndhII&+ff9 zVP0VMp@1yb-GvJSZoDnq)pnsLht=70$5#g(!Ee87SF~MtvU;J{Ql<rKx+k)1je1*R z)ymc3#ii3WY00(bw9S=v^B1$YJT;tBYB*`Z<(GHzwy$`X{7J&=(=A@FrI(%-b-uj! zL)WMOzvi0juTza?`Uk!{Ua#ZxN8#P>l7kUCV(YK3{`f!I$5DJ$oXp)k@!4z!Q>+u4 zIA`xLoD!Vav?gvnGk*rp(<td}OAZCB+nRVf63Cf-qakPe?HQTA_kyes16?j|xaT6v zw&i+XR><5?lXqUZM=$A31e^2D3yX^q*fS-(o|cLvyU&+6HBrTLv3%J2>)Dm&S_c9G zujV{G`kM8l-XqiN-Jwal@A@5TS@`ADym>}5G^d~bTX+BSo|h$B^GZ3jlUKAHwd}!& fS!guZ-)xlsFhfaN^^dkFD0O+d`njxgN@xNAkfJW1 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_close.png b/Telegram/Resources/icons/player/player_close.png deleted file mode 100644 index d27dd6b704ef6e112574d72a2b54f63046ab6bd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhKAtX)Are!kPTbAQpvdFAcmJlA z_Kz|9n%G#LIZagi_d>H#WwJ?wg^KRU6<W9Z7$<Gsr#q=|hQE`Bg*odM?PRm<vu~ey hoBhXNUE_5Ax{O0V?6SAy{sWrJ;OXk;vd$@?2>>yPF2Mi* diff --git a/Telegram/Resources/icons/player/player_close@2x.png b/Telegram/Resources/icons/player/player_close@2x.png deleted file mode 100644 index cf0e108d83145c51bb0c33ef93b00a08824c43e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fJ)SO(Ar`%Rrv`E{8*;d`?|m{y zZ^3sSANy^I+^w;`Gr4|pTx?HU#K<r)#D(L4e|C@Jr04UNOiO)cW@4=BS$s}dPVm<j z*&j`M)#3kZFErjiVrTWnLBBve?n5qni13Tl%PUsJWd*nIvhXdeJ|(`;Dq+k2xK>x2 v)5V)jI+iZgbv|KamfL*qn<?YG^IupcL}$xfnWkh5bQgoCtDnm{r-UW|i>yiy diff --git a/Telegram/Resources/icons/player/player_close@3x.png b/Telegram/Resources/icons/player/player_close@3x.png deleted file mode 100644 index c6d8846fa46de551ceaac2bbe54f11de316c799f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 zcmV-c0jd6pP)<h;3K|Lk000e1NJLTq001EX001Ef1^@s6SLDKP0003UNkl<ZScS#d zTdKq$3<lspz%A<jw7RW!AHshgiZeY->m^Yd@I^xSFrp|C5r_!ZT6pil%qVj!1~Vgs z0Bh}a{siQl0RS<^We{dYN(lgvbN)iEfD$VpS3rpmWZO2}#Z1T@phO4(R7;ovsnt&@ zmHPEW4WturB33{K0AIvR$ROZ9Vg^J2_(f<SLcl#j0#N{-5hD;KppNK)7-O`6HlhXD z_gxF<L$n}DKtG}vA^?mbhzQ0Q9LMq48e_~TVE#%7@0$60LIBYLo?0^qkpogn)pKG1 zA_MfEn4J(EK$_n~thhxRkl#WvM)A@LA@cwV$XtLBG9NGuSp^t^c<+}1dLZ6=kaO<U ox2SXO+!I&GE3fjB@aF%-AM^QX*Wfk$MF0Q*07*qoM6N<$f-@9_$p8QV diff --git a/Telegram/Resources/icons/player/player_forward.png b/Telegram/Resources/icons/player/player_forward.png new file mode 100644 index 0000000000000000000000000000000000000000..9a28263a77bcca07db5cbb6dd2569bab0184b9d6 GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUP4;wg46zV= z8*IpRK!HbR?%Vn=%zo?a4#vM0z7r#}gqN#GgOzo<nDN}{PgK%v`egSPJUG?3_zO>3 z{Kl=<s`Xn|h_*hQ6Q0I#$I^)-=+vbP#==flWRkB$1^Wv+rTKMO1$!Nvp%pSSQ*nyJ znkiXZ&OO`nsl$A|WNz<G?s@ku-MKqoUpICRyVRc0lbCrvq3rQ{s}BsZ+&89Ki5t!Z OIn&eC&t;ucLK6U$N?Akz literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_forward@2x.png b/Telegram/Resources/icons/player/player_forward@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d1de2c67ac9aad6150d077e95e896fc5763ff498 GIT binary patch literal 510 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GuFmf`8*7!twx zHf-ZMR|kQ;{;L;VUiGrJ(QWM~?z)rd0`U&(uWP9ab!t@Pe=?iNQFeCg_otd}ipk&3 z%)GfdyF5<6E`l+vK{SCCNIh80RTFrF=Wxd&jtQ0X*1xWjaymGN?OtJ8<hQ@Sr=(`i zdZx+BS}av@+|qZl2FJb9*yw4cDGL+&0?lnBdJSeRE0jq;?t1Wz)4Te2lMbfe-nQ;_ zo}}gs-MG_sYRvxEOXodT)Msr@h<$7#Y4Lj>f0M(8V1r$y343%Te;hC5IV><igh%L= zR9A|D$@yZL<03tIvJ&h4*cPAO6ky;vNn!aJg}GNI_=x?VRxiPJds}My-UUYYlCH4F zpL=V}x>8+t8RHBSUwv++vypr4=WSg%|Lbl0*9k4F4}Y(X`E)t;Zlb`NY?;ZnuZ#Zv z|CW5aCN#A4=7N(3yR=(BPWd*a$<e9RahH_~Gr#pp4X&G#9tSmEJy3)NOM~bSZPq#l X_CFp!(jQ1Z0ENA$tDnm{r-UW|Gi$yL literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_forward@3x.png b/Telegram/Resources/icons/player/player_forward@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c59050a842c682068cec4f0db064569b0f22f65b GIT binary patch literal 775 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbHUhz*Okz;uw;_ z`Zm(Ki^)->Bv#<ai3?X8cucOxT!|61X;`_i`~#QVfdkSNjJ%>pFI-7rRu-(^aN8o_ z#F>)wudkmwaB6l!<+qTltHbqfU0xPc<jFLtK}CVn1C#nt$yu}6eO1`%!w)xH%=lxY zzlg8jRivJI&DE^CdE4(lFWrBiy_wN<%JD@S`*}A<t=+ToL%E5R>#ENY6F0qQv618N zX-Tq?n_c}OBJHgC{i6o^l8k09xttkmEH9ydR%dRM&g%~%I%%6D)h2Vkm10RgdVp(c zSGHiAQ}@rBd8eN??R7pFab;tZmEpPrz3U=$#D3P?d+xw~tV-jwy3l*?2t$$CXO{(N z?D=KTP^f*PDzsy78-MY}`|rDtD&3iJaL4Q9#(6G`!V`}^ELahuHE+3x0mJ$=D$gAG zEf$Czf4nhlwWb~`4^KqKESJV_M;?_}na=iAk}&9>dc@{M<GnABs`g$w=%5w0{IcWy z8{8&-3oPcJe_C|2Oy%vj<6#<hHD<Hley!T}>wkj5?TK?faQ<Ombo6PF=2S1gvX?0( zOU2*t_vdZ*Hh<Q&<oshr`_GT#YOh{<T{?AXs7%7neVk!6yIi+g2C-Yrv*=GvJtOL~ z<;CrDpCZ=9t^Fa=ef#aSk2i!;A|;Di-TAh~=t=U=TBsAgKCI?*!mjojxznd+Xx~V< zSfFz_d1uwuN3V5e@n5RY(N12MaPOje|I=4jyY|Peul*R@x1$my`k-ug@80>pN}GNx zX6Zlr`Pb3Kn$vFifB9Yeb)WC@%XjnKuRPq?@rY?MiwdKsgU|#_>cM5<AI#_SRYjj~ RJ?aZegr2T`F6*2UngA!mNPz$V literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_full.png b/Telegram/Resources/icons/player/player_mini_full.png new file mode 100644 index 0000000000000000000000000000000000000000..a56beec236b4f0e1220576f53b1a1d11407efdf1 GIT binary patch literal 540 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgftj*KKF~maf zZLn?kAqSBw!lo*&9b#ch97>FqJ3{`+y>;oBE5y2SlgF(SjR`ORPbks6dPr%37?%?l zH+SHnyLZ;!)cNSQ#bU>y@I7;Dt&M+AobK^N^8ZuTS!bWs?U(O$d*bl3=H8|?QESEe zkIP#m?Y{f(L$g$_ne>&k&5>4f_nlsSHS4EZyz*6!rVH=C2X4%r{n#Rk-@#~R&DX`3 zUvAJl!ts4l>P(K3Soe)_>)ETimR-*DIOFFy+edBQ^U&3y3G<%EUXRtAelcT;RSbjn z?qdp5y_UW$tG1AF2|Q9_Aki@G^wW-iPm_v|8_YlNd-;g;)YGXK#JgFG76vF>V((F~ znCo|9V?@s*7bVA@rkYyWZA@`yvt!qW9e+HrrsA-HgN9glBdeo@<!YY9g0{noUA=D1 z675mzukXJ5uKk=#@SdL+-<GZKHJaJ8NFyaYG}Q6M*Q%5%?d(~{sy1HnT3Tc`U-r1? z(x9Bb;b*p~s`egF&S~(z_-u*yc83?sS-<|ZR`J}WTD|V?TkaaBZh5!W{FZ^`pqTM= L^>bP0l+XkKpZ?V7 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_full@2x.png b/Telegram/Resources/icons/player/player_mini_full@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..919fa5588c885d79c158cafe51e7360af9a54d60 GIT binary patch literal 995 zcmV<9104K`P)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NFO-V#SR9Fe^S3O8_VHDP|w4otF z8j2tyN(fp6r%JF!i-sU*i3SHji%KKgaxljXp`lQumX?MhTB4$eHnB}{s3BSku^&+? zzwf~LIGkSZy?vSv-^abv^Pcm(&vWj1-}nA#H0q^xK<$9q0ks4FcL!!>X42{O{r&yU z&Q4ofn{czu&CMGd8{u%+Y&Hw2nhbqD-!FE><8i@eeSMwsXJ==<y}bgVn!}}~B{Cx* z7(ja{1t6c#I~)#yQ=Os5<Ka$(1$9`mt*tGng9reiuLA9-!-a(fZosjmw9#nX-rnZ4 zsi`SRW+lP7xj8N^U<E~|(@EsFx3`|29tHB>rrd5fiAn-xq1Dw@nss=1C`r~zMlBXg zcXzi|`y~taw6d}y$)_Mrfvtny!C(-c2$Th|x3@PjF@ceRfq~1*OP-a+e!rjYqg;TB zblP8EUneIgxsAzWDi(_<8;iwwN)~6c*>dY3K8o_wUMLix6Wr|J-~joslNuWvc}g0S zdIbO#VaaxPcWHVek>E}?n~hR~gM&OJjfq_W;EH?;4-E}bO0U;L+fdx=^->%`oiMKA zaBXev<>dthc_*NnWHO2Ij*bq&v4#cZa=DwE8}#ey>f*+akB>NPX=&kcz%?ug)IUBx z&`+n+apStWKfd+#^*j!^h6RCHUtb^k-QM1E<Ic`b9A+{Z9tT{*g5bqYD{^&pMaB&c z4ejmiIDB|`ptvlk;Be6t5_P#;@9*yzzq`AmH3C9^e?RfZ#>QxhEcp3@(Q37lB|Mi$ zM@P9?C=|l%=jUf@Yb#Gl<3J!la^(V4q(>iYYWQ;EHcd@U&(F^&dwP1xQ;5H8ehe)y zFCQHp!OK-(!w0CKE)ZyVa&j^~Jv}@;3}4gN*VhyW080rqVI&fX@KhyueturLXUd`@ zBO}l=R7j;#!m~hmu55O3aZ#Zr*=ZULe7(RSr@3~!U4dL)3SK~@D*!p6@$vDerzawB zZf?pkD~wD2a)4i{9Gs)`^K&W%!13`hykAS1KaIfN=QfB-MK3NcIBkD_-(WD5(y9~r z_D2zHs317)v$M0+F;-Af{*{J_uw>C_6uyn<HKL%Vrqm9o9Z)-<cHkfAz;7?$aKD|5 RdNlw5002ovPDHLkV1lxgu^s>b literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_full@3x.png b/Telegram/Resources/icons/player/player_mini_full@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..554968fce980183428d095cffd1242736c847ade GIT binary patch literal 1506 zcmZuxdo&XY7~gCdA}LA<R~{46b`MDu+rriecQNF#m61mtp`?pC=9N1tiMF8!5@u5! zI%r$8cEh|@Hlhu;6~mBM-MV-Fx%Z#WcYfdR`@Y}robP=9d<DKfp4ytbH30yCHrfm2 zuVk-HKva}@wAfr%Nx%#Ko(KR#VDwqppd#^TlD9Wtzp{n^fO+Qt;7yAXyOjU{K-EA1 zNXfv>S+&Z4s#i7W|9aC{0`ph^093JPlshh3buL(JlNq|>M}@v2(j0N4D3Y((Y6=fE zmGVl`E#aZiACTksPkZ?>P>5WuVQmDd;x#;XAtW`XRsMZ`xpM8>-saX9bBO-nZ{pZv zvGfb86H!%b{U(c6`%QlmihmexB+|#3nVIF~WwBT+kwp0VmRxb2oSHh~>^w9yg!T6R ztAZ^a6$-!fudzo)M%vn(Dr?Ki3=qd$UE|~9Q&Uo=Z)Ej-7#MKF5t%F&clgWtdVWQA zGlS96(sJqVaK*^mw|;sB8!Ia{ekCl{VpkXF9^s&)BPlkvfpsU0?%?2nH@Kc2GdeaF z)G{3u5^^CaNt!CmW4E;2w4$s>rkGn=3VM5WE!EoF+l`Hl;lF1w>pMD(PDlw&ng}Ga zuC7iz-_z4mR#q0=-hhSL*+tewe}Ni``20tWjq>l`>4gJhV+{}6V`!6;#BzP;HkaQv z9%*Q3FxC8%BI0qz(&ln9nf&I7%57(Dwb==o%t|n)Q23f0I-8mK!Y7DTz(v_t?Qax1 z!8m<=eM_N5(^NW*HZ(lk+1V+jjPRebSk_Re<XMbd#iutxc~w)BYX881Z&Z|QaB$n0 zR<KMadvO20*Y?Is3<ksA-oE<*2m*n$wY61aY6{UPjzCwvgUvpP!I(Ae>gMr?mJ+!< z4m$umiN*S|gzy50Xu%&QHIvvN%K^OCuVLxQ$$G6b*;SR5iKhCnZSJA4bC5P~7gtw} zylkNm*9bI@i;9XGdJN+D318vC@=t`GldneF@D)^QczF03QgSj}r)I~L?hFxgwYhoW z>sM!E6@PO2_4`*3ySNmc8y_FH^<0Xsh)N$9t|TNRAm^lXa?Ud*GyBi7a<J`z1A1Fb z;cz|bu01HC=_iS#n$0$`xlW<*YI6bu0+_?&<9n4FhFE>>>{+nQ^5P=5Hb;3iYnaOg zDg|F+Mn(p{;?A9&VXtdysnk8atkzc9T<{hsOvFDkm{{Ibn3<8$Wn`)lTv5(uES9FH z!K*Kvv^i;MX(CX=;Zr(8F>VE1qW4)~_Gd6vuh#hS*jPx}zVs~?FsFulAJ=MXECQag z*~ije8X7pl9P_&yo0`<s)ZV{;pAW~AGcI4wN4s6R<irI?a@469SlHCtS9tftM1?@0 zqhHm^B_ktyZLO`j_ezPYbR+fedy4eq;^Y19MV#gvTrii*WS+3DmoM>m)B1(N9r|8& zw~DkE>f?GlI$RJOKo}Bcr~?s^b(WtynVW+RP(~;0it?1J%?X5skW;6~AsV>_Kj!Bt z2Z1T6sW$=)_`eTk@4Xli5#iyH%Nze=hZgAxjb`M?NZ#6=7izZUDU<1o!*w4KH#axe z8NkXDN{g4m%ik1lM|X+|SS%KAwa9sb+Ix^fq1=3D0#0l}qilfA=&Q1lyAhJ{pxKL# z^xTqtfjAr#jh^JIew`piMmiii<avC4cJ`a#D?zI=0|V^GLBTpAQRC{`T9)*de`>0O zy`ent<}O+ny{c*|Ec@8eqeLRHG$pVKR998H`(3GtKIMZ*OVwR6IBA!(1UwacdxK_e zVg0Kfjpb*x;=hw$IBL-Y-r!nJs7Kc;;Go!!fJDIqw!JNU@?kBH=TR>bW=-hXuH2>5 zYu1_Myye49U7v4P&-VY~b{d1xe6C+yTuiVhl1R0IgPQ8MGC3*>3e7Bz3Spm_nPfZm o{CURFqt~pl$N`k<&qB!Hu-R^b6E;T0ahv}YecT7dKtx>o7jD<G9{>OV literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_half.png b/Telegram/Resources/icons/player/player_mini_half.png new file mode 100644 index 0000000000000000000000000000000000000000..b73618710a9b5cf03ecb73396a0846a653616200 GIT binary patch literal 412 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgfjLp-<F~mYJ zIYEN8S(W9~>C^T#KMeBk*>uT-#K!)WXFFlOV$mX|G>#6n3<2&KR%`3syLMT%yng=t z`kgyG)0+&YOqtS<wJv^tpIYC!y(KS&HVI$2;c%6mU+&NE@A*YVM!Wn>>}L6y=ml)- z>pQnO{rtX~pNGz$?_Va~+PzG;mphP)v#z|nTx4znpYSpPxry=n>ljmK&YbyiMWRD7 zla%<1;IOc72b<aL>;4Fci5*M7FjYJJf{Ai*qgDF(dAuT;8X5<(xRhh2G&eg32MZr! zb*p!FW{%-Hk&u!iA}&6C+BCcJcQTtS63aqEMQ3$$ygs;e(XwT1d188|N4kR5FAHVg eUSXx=%)pTF>aT`R_J_xyknwc&b6Mw<&;$TORfnSh literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_half@2x.png b/Telegram/Resources/icons/player/player_mini_half@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..34c4263dc2168c3dd426419e7c9a4b6d10aa02c2 GIT binary patch literal 665 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2Neyz{Kq7;usRa z`8Is_I!8y5uQ?@)Lb?K8ggPw@d#%DHD|lW(N^2=c`~m(S92YsaHXULYnAmZA@8mbL z=k7drM&kI1ojYd+zuo+O-qvetEN5CKZ+_`+y+HN?Uk3ZX?aViG%J#;+FWddG;?4Kp zy7O{2-~6*^-qEDQr3<R|`gQ)~)D;d{8`hiUaZJkbY0=D;K<a4H#O24B99x|n7r1pQ zh!<a7d@;j7V$1EfC8i==TcdQX4tMX3%U^z3@mJMextoF&xto6GZP(uB7{J<Bx1Znk z_+!Jley4>#|N8cOZ$nTF)0u+_4Y|)hTZ(mm+_B_SN2(G7)25WkCt2F{r+aHA#R)Ub z<jC;((Zir3*ME8G^(0}d0*<p!K34FxGf%no)+|=&2%qEIvzK0$M6C^D5aM7_xoD@+ zp|K-w{er_!liusQO_djUQnp)pB1b@;nY6OG@n=WJoiXR0e`b80W0ri$TlFKisi1<R z-TeJAdJP-y=E?S%r~UhPY=(zQfzkKhwo|<pURciP{3FAp%F5@?gA%KS56gB>{J-O6 ziPpR2tM~W$>r6O1PiEZ@E=LO)wg0L7C)>kq`i*D$7|oR7YybT7&qDX7*(*a1ZB1a) zI;pZzYbsYTmx`*R$o8vQrt{Q}`g|%lqs<<u*}q`l=8G8;wL$)$g&y7CrP1_df9ce^ klYV#CfMaP3^D~(rY#hl!SM5whJV439)78&qol`;+03cBgYXATM literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_half@3x.png b/Telegram/Resources/icons/player/player_mini_half@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..308a16a04e507f9a2bb6e9359c83cc98cda9143c GIT binary patch literal 951 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}uITCF7?Q#I zHp)6%%Tc7SGu0rJOIqG=NoRl9e@C4)I+NTksjwODQ%&MXdiY;}sVP*-`(jv-=PVV4 zjt&K}#)uyd7w$0Mvz~Ki)l6TvO|NBFzdtk2_WAF9cjwNHEzkD;)53g+@nJ)OgN1^O z03QcCn0w&=+XFXmM(&N1=WD;4w>?1PM$R_h56W}>>TCY>9)CP}vT$kk?wvb->b%PT z^5@T*q{NdcMGLhg=A2E-5*6z|K7Hk^{?$PdDngNKxwftHkI)eL6*To%3v08ZSg4k; z7zay2*bS4!y8ZKGr)KrtIC`{ICp0MW_U+qumJ7dq)%E-L@3{5fA3Shad{O7jm6s*c zr%yk<P47;t)R`2cP8TLEk?x~0de`%|uVnf6@Z-nA_1Ba4<fJSWT$i_9`u&f8|K5G9 zIB~7Cv1n({?xG_tg=Vvtp6YW`UVhnA=o%ASGvn2%;T|u7+;igO{e%}^^eFnl9A~to z)ZqH=-PRGGf-Qg6$UQGO7Gm6|==$Zwix)3na$aX&X^<b7dB^Ear~b4clbJq$>*ULK z?%g|e*~b14`x`fWKQ?o1*z14)<ob8S=*gTq>LtjwM_x|Opt}2Llk?69$3wHus-@+d z&8{!HA3kBj$&{wn%&oWP>ueQKIg?<JkT};b-D>V5!APLO2Wh=450`*6X&(Ca?OTFj z?@fC<yGIk$kClt=n~}70_wHz|sRi5>d{#C#OD^qb-60&`qj~G?H@C%(SJ#JVS+pCs zeZLf9AE&7H>(?)@sjd4Khynd_HEU|tNo^IEhnAsHpa1-sqq2t8LZ)x~cJs`~^ZOHa z>Fet+QCL&E_@c(WU%7jJG|r!L`SsW3%a?11nH$XhSz|ZpJjaJgOy{g3RHN2ji&|S& zTDo+B`R_ODg-=eL`0D=6n-lr|{X6mXtEu8yrn>ot*3AVLXOuGM9gYhVoLO+!I4hVX zHc};NqeQn^r0LX2Jo)zfuPhT;S5&#?hjsAO>k`-Yzv0giVB?ONv(j#k*HW>0leu=W z*5AHz_K!?b-tj-oVin5!1LZf={0``rZN-*Q?b-h^>|)&La3ZB36qIQ^UHx3vIVCg! E0E!}{IsgCw literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_off.png b/Telegram/Resources/icons/player/player_mini_off.png new file mode 100644 index 0000000000000000000000000000000000000000..a650f55d2e3146b859badac7913064240ca8b648 GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfU&GB?`46zVQ zPLN=2R%JPL`m}w`4}<)BHeE6yv9W*U*-n_RShR>KjiW;?Lx4Mm)!KUZu3c6wub)4^ ze&-I)bfw<HmzPvi)E8{zoOP5Vyz&0uo*s^=1-d7A)Kd6V-yF=jaCLRK=0_<d4oeFQ z6H`;gG{(jI7cOMH8Pn9*yGEilHZ-(#4pYDrkEd6zTsd%7DO7(~)vJ`Kl3@3rB5e!| Xt(hrPlQd16K<@Q)^>bP0l+XkKq-|Sw literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_off@2x.png b/Telegram/Resources/icons/player/player_mini_off@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c3150639bd2378fac60b1371e845aa7de3eb88d3 GIT binary patch literal 450 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GuFrsL`27!twx zcKTV~W(ARktuJq~+DP9$F!{7agW*3n+g;7VJNmgRN*Q7osBSbA5fsv_T~<49!u0Py z{wOH4HS$Y2FlR9E{@K=Oz;nEC&a##7t8Z^>wBNSwHTzed!wj4u(^OeMXEiZCt=zX< z(bm8rb5>DiLc=2qyG4u)U5h*%J_|0eF27cLzg*_HiLceGU3r&HO3qKem3zC(lw;AW zj(omrMW1b=r<JZVQU2;8$T#6sPyqX`xj#78YI3Mt?rys<;Y@?hxk%^Jn@(&pIcDh) zdp%PoAZ*s=;K@@II8Hn~^`vT^`@hpO{GNr@E>Gj~ikqw=IA@JkdS6O{qs3_>#>onS z$tyIvl+}6{cX=1DmoW(5))*GL_4^jrM5*h$G}iI0E1lXlQ(Q)rcLAfxhjg}iyBjJN T&kAK10fnEZtDnm{r-UW|6TY6G literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_mini_off@3x.png b/Telegram/Resources/icons/player/player_mini_off@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f4792b216f70f6bcaaa4ad5b9cb04f72165394bb GIT binary patch literal 647 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}_R-VDF(iZa z?W~RKnhZqRzCQEgvohRqDx+up!*>h1XU`H~4eV@V|D;*NXl@>V(87Yt@80bt=5E4K z)ja{H{?6QBYx{Tqe+JK7Z`GIr2EGG~77XkU7-SllL7c|_ZyomEzyJPwponb$@v~`{ zv$l3UaF=MaKY#se)!P!Qw9RY7R;RDnAK!lXV8R9k4xyD1kKdLVi{@;M=vYy)dt$=o zn?8O=tIjeP$9`9xTA48Uq)Y251B2s*H^LWYBs~5YG56GrF5Y%$p-{7g`=9uyq(<J& z+kP=)%IT*^PGoJhlInHY+v)tp+-&yQ%P*JwxHaLB$>!Yc(UW4nKHxbpN#4Nj<I|#* zAzGOoCmvj!+PINxiR00tG=^_eCzZU-WL&nF#f5Q^R*4_D0iCM3YRAj#$MmiEO- zx4a!h6jn#b96yp{$u+(4ZP{)a&A$t#l<lsyn}0Pt^3_Cn5lt`E=xrMezgF$N{`%%N zU8(rhSJ|2ySA<`gcW{%diRr@FzweJ!dvf0V+UB?X^6reO(tEQqH&@SF-gG26W93%& z-}}~|SQe`ld7#EFJW=`i*~1M_i+)bh<+r+jQQ_t}lTWhMUmUhDo;|>GZ`IS5*I%tZ z&NQhmp1WD}vjt1~WA=y}P74FrBjoN|-8qaB8z9cTq6gwf*M82e3SpK9B?3=ZKbLh* G2~7Y`Dh~Mo literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_next.png b/Telegram/Resources/icons/player/player_next.png deleted file mode 100644 index cdcf1f3da053fce14c4771711760afc74d40de3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmV<103`p3P)<h;3K|Lk000e1NJLTq000jF000XJ1^@s6phwqS0001@Nkl<ZD3P7g z!3l&g5QX8lh#+`QB`HC$CrJ$+>?x221kXaM2*SUoENBuBJCM`IW0=h7JWr-+LQ?VG zOcDaPu8VEk7>417Z}to4+V>r6?JeB0A`FmHVp$ftu6xqWs^MJT`xfE7#~8y0K!4P_ zu4;A;Av8OO5HQBnXLiof_kDHdY{ksSaWsGXX~mw$acu7SX_8Qkk$Il07xZS5_yWkK Viyj!lerNyy002ovPDHLkV1nf`T_XSh diff --git a/Telegram/Resources/icons/player/player_next@2x.png b/Telegram/Resources/icons/player/player_next@2x.png deleted file mode 100644 index b646b84724b92ae50a5d359af7ba70ab0f11288a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319 zcmV-F0l@x=P)<h;3K|Lk000e1NJLTq0015U000&U1^@s6*JCWt00037Nkl<ZNQuRk zyA6Xd6h*Hn=qM0k1qy0<CSVP3KuymYE?EGv03#s80*Domb<-q~6G6bQh_B@G_}EYW zJZP;M$1wpojVOvJWdRT*IZYFbqHqF(AfS{5Kp12AN?q4Z;(gym?0d(yyz4rqX?ja+ zj1jSK@wa?g7M5jsODthO#7AkG=9bu%Jr&y%_I=MR%WjMBus?TfPq3~ltExIY)e`oy z)Zb~_mSGrPimzB_F-eZ&$UM&pIHMn|JN=AON{Uh`#Q?tIx>_%#{>$OAuf(=(;_Gz8 zo{DX77zXhJa>ibk`Y%tWmrugWx9GJInCDsC`bby+gw~o#k~o8}W>3Nb-~+R&_|AAz R=kx#o002ovPDHLkV1i<piO>K5 diff --git a/Telegram/Resources/icons/player/player_next@3x.png b/Telegram/Resources/icons/player/player_next@3x.png deleted file mode 100644 index 7650bfdb7b7c481803c48a20d0d5d720601b9aea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 554 zcmV+_0@eMAP)<h;3K|Lk000e1NJLTq001oj001Ef1^@s6P1qvg0005;Nkl<ZScTQt z&#G}z7=ZER&p?Q&MBIWT#WhGt$#qCcDHoxnlnkU?f+Qu^Ad!p_hT6M4XYd{R&iRgG z+xy*v@2x$3`qf^owOVR07!U{qP)dn@c6F6fD1hyDOD2;M4cpaypaFDUr_pHOa=FCN zc67g>?=zpziN#`KXzA{cXn^ziOsP~7MN4&`qVLn|_3-=sVrYr(OEkcGy(X1PiK4A^ z4WR)vO`}$;;dDC1&}O@a(f657r$izVF|?WP+h~B}@kpUi5JkV!HHE%Ur_;gf^@^d5 zb}gX+mdhoHL_!q(s%seyaJ^osR4O<e4l(p^T@mzs#^W)eP)H2@tSgEJ*zfn`aye1- z_pUe^;C8#wYPF=ZkFLk#!2zKDqZy4x!vFDe+U>R-S=#OPpY2dGdN>@&=kwxg`tPoi zT+@@ugm5@4zI%M{il8-3qgt*0bLp|GYZ<*-tw<)5;wR#7T}$Y0w~No`6Tf@B>Y75I zPA7`RqWCS(X!mXOY&IhrjT#Eu)isRPb)9;>{?;>qU0p-y&1OS7oqjKB0ByGW65a3j zO+Rhf)qRS-TrQN$W%JP|Xo>ER=*40|JRY|kwyXOEt?N3?W)rvDEfTh?`#|q@JF?lV sT=WxdM^`C@8V-j9gF*4IU0tOVAMug*`RQ-1$N&HU07*qoM6N<$g1^-k9{>OV diff --git a/Telegram/Resources/icons/player/player_order.png b/Telegram/Resources/icons/player/player_order.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2b10e7100063b51f817c099fc208c859df68cc GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUE%9`546zV= z8@!SCh=M@3(klHI5^bgvZajFND7DGq+CrB_XZDw>t9H03Dry!s<`kC|b|k*YxP74g z!nC_VP8~BDHtpm+WBFaVLF8j+z>MRUqE749I6W)Qd3Me)-0P^sXC;prufJSfl=Waq zM$BP;PyJQ;A1?JRnWD&IBl*42;n14v@8?Hm&kahFXW4YGiSrN3`&z@So=6UbqgQi( hs^01Qdf>G54`z+m<tOqpUfck=+tbz0Wt~$(696UJY3%?2 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_order@2x.png b/Telegram/Resources/icons/player/player_order@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2ae4fc7341dac42bd5919476eb1cf586c3f5d007 GIT binary patch literal 527 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GuFR_W>D7!twx zHe9i<#X%q@Ac2$LK_R01@Lb1)iF?}k6Dyhe#1#b8Ie!^ES$9ZfwqHitj98~4&;A=% zLc*_`h?*UEtmrK?fklPU^FcGqyz}XkROBUMZp~Wu^87XiJ=40=Ivh=fGWFY88{S5n ziJR_RcYE8~S#E!mxP$WJMUH;o#_+}Kbyn>45~)_lSG@*3K};{EmW9UL-?i>{qr)rx zx!Z2Yt_*Y!G&yg)b(w@JN8hcLpFEO}SI@it-S+F<WDbSo+LbdzBPNH223}U?k<9k} zx~4%@c3P^Z%!fy^$M0mU4u96R|6O*|?FOEdt|*~NEk2wxUKR0sIxS=I`KtYXq0RYz zxfN5|&zM}@X=R#f{UPYK3GdXmhAhX*<FCz8TxlJAPvidg-Rl;6bp<v(SaE&U@@uo6 z8Su0xE=X#1T(l%}i{9M&%>Ks}v(&7Fw<)aczhWLydo3k$n@;yEPKM~cKV`3O()pO$ u#9DB?GX30-Th}C)W&p#<0T^8We3|N()Lh6}-Q;lu6d9hbelF{r5}E)^vB!h} literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_order@3x.png b/Telegram/Resources/icons/player/player_order@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a372bff445d7f98c3246ac4e86e7c763ff64ab70 GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbHUhz?A3d;uw;_ z`Zm(Ki^);oYr8^*f<y&_u>S(KdyXPsg}NSS_I#0OdZ5Z9qMwkok1dT$t7BKc?1V!_ z*WS&&C0o6cbK0v{;p^k}a-|h23D#a=EOn3#U`J37>MaC#?c8kS^0(i9AlZL>`)yr| zhr11U+MUldvme}@!L@Gj)vU_k%KF)7(+bXgknB48sBZuA=7$|AKTb7icr3rX`f8Wo z)1w}bPEE6%>BGm?+`pckv2Pns(vOoVhn9ab+Oa9OIZ;CzC}Q-ZZohx&2MLDNei5Ro z2QRLQVT)NCcK*5X)q}|l494$|OP<M2o$#}!Zu89oSsP}F18uRDI};c3=uGrDlV+@U zNT1oj%crWCZL*f(nZL1TN`IvC+`qLa#^;OoBDci{Z_iE%_%UhD=RG<<<ph5Jn{{v6 z>C`Hde($A|EF-uizsygt_^Y5Q^_O$G$obfwqsQKv`mtEe5YARoDosAZqwaK<F#C{z z*|oQ2>#x5)+%I!y4LgIE)m*;zpQ%m~IU6J9_^I1F)>Tf?neM$m`sbhK^?#OkFV}tj z_1Dv)hg<6;8v;K~d9{5~OjqLaxb?@g{~9q!U+OxzL!|iC#f&4TE7=T+{R~yEC;G{D zA6<4?Gkt+1Fg%`%IJ5ovGudnDv(J`uTaN8ua`0I#8u)>~Y-dc~-nh$^dn7z#B0DFt zbk`*5ig4Y2JMCPX@}x;lhaas9(P}?@a5gVvU!vy3_ub}KKUUm%TedaIcb;v`F(0+b pXVVT}U2yj*dO~oJ{UOb6$7ta_+vsz0N;@b8db;|#taD0e0suq2H`M?D literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_panel_next.png b/Telegram/Resources/icons/player/player_panel_next.png deleted file mode 100644 index 2649ce5d756f16594db8ee80faae1eb97ea7c422..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^f<Vl}!3HE-RKxZIsb)_X#}JK)TPJPgbynnIT`S__ z)F{y*WGK9rJwZuJYTJT6@qax8p9HtqoV*&TezR&)@v-Xu4xRMlt}KihYXzMSNEqwB z`4L^@9$I+LcaplDCqvfSB~zwN5{>s_I3_VAXv4L2AK3qE?YvxFf9T7-PQy==gnyp4 ow6_lnU7qyz$IeZ2SNvsFTXRa->Bu{ApmP{JUHx3vIVCg!00$dJ@&Et; diff --git a/Telegram/Resources/icons/player/player_panel_next@2x.png b/Telegram/Resources/icons/player/player_panel_next@2x.png deleted file mode 100644 index 2c93ed70eae54b406d666de0c706e71bb3422cfe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmV+^0oMMBP)<h;3K|Lk000e1NJLTq001HY000;W1^@s6pQbMf0002+Nkl<ZNQu># zF^YvS6h+@;8{!6Bk%+hu7h+>&;gT%GLI`fcLe6h~{_qEtB(E@Y;gv@Y#2cT|_dQvb zAtF(u<v#;pt)(nWj4{Cv(DGj*E?w7A6h*LzhCdYnn5K!QX`)18_)8Ihecu_!kvz|X zMMC*o5tn(MsjBMs?6tg?2*7b148w5Q3@z_1;&RmtEgvl6^0ygUK3W96o1x`ZBJkS` zE$^E310WA(T~}$_R?L$UA|l4T6;e5x|NF8&USDhN>2<B;y+x9^;+JKiuIuQJo)3R3 vlEfO2#A5XLs<>^N{{nL&qTxT2%J0Mr{--M-S@lk&00000NkvXXu0mjfeBXhE diff --git a/Telegram/Resources/icons/player/player_panel_next@3x.png b/Telegram/Resources/icons/player/player_panel_next@3x.png deleted file mode 100644 index 05ac526903e451cc8d4ad3c931ba3478d4bceff5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 629 zcmV-*0*d{KP)<h;3K|Lk000e1NJLTq001)p001Ni1^@s6Bys`?0006!Nkl<ZScTP> zziJ{;97bmrEs{zkC>E9$S_?jag4PL=kT<YUpTI)jz(SuuFpAbwm<I?<FPL&bNf?t( z3xzo;b_HWHX8zpYF8jf4@8{t#^NreQG$J04qiLGRnM@`m5(%;UKOKPOa!I*d7AXL* zZJXQM+rI+?;5ZJwUXRPmOXV<!h+E<$i^YOMp&$YyZs|)Ku;1@#x7$RcQROhAmR#Ea z(=^Fuvm!7emd>^T+wGP{qk*pL%3+jS^3(>*W;4?1v<M6+vgEl9cz=KA{{CJB=8#a9 ze76DP@!0dw21#H`f!cu8YDJ||5rH|xx1}&;j^ohp_s<S(*!Z#(x(#@Kes&#NRhdqw zB$G*b82CN3olfV=(5j4W+uYsV$-^9CnkKniP6Xx<S4$FQ+wGQSvx%<j64wJRmc-hC z$Hzy>>w(XfbO7x?&w9Pq>h-$z>tFv66>%I#+wb>6Pw1Z*h9P-901pojTwPtsJu{ol zhFYyA`T}@;edXrnM)n2p^z=j~lM#J2f3hI~v)k=xwOT|X5wX$y)`n`B`Fu`3pBMd{ zakZiHL$llMa&d7X_M!Qs4HYn!Wl<`XME?SKvJn<$Fc=Vv#XJ=Ri7y*LVcy=}s8*}K z3jAg`9Qv~n1jaB7QmK^h0<U>_dExr{Iv9jknBxHqQs5<_Y<Pt^9YEDEN^JZD<2HaQ zVU*c82Xk%!Rlq2>@g0np0SpTxV&fFXvMh?lqDX<bjpO5I0D~NH8%O5@t313nEa?WO P00000NkvXXu0mjfcqkv& diff --git a/Telegram/Resources/icons/player/player_panel_pin.png b/Telegram/Resources/icons/player/player_panel_pin.png deleted file mode 100644 index 809ba4327862ba7d1493c958779cc9edc9032709..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 349 zcmV-j0iyniP)<h;3K|Lk000e1NJLTq000gE000gM1^@s6A4o0H0003bNkl<ZD3Ohl zJ&J@d7>2`I1}huE3kWuXFboK3EUhfXY7i`K1k0)B2-D03Jb?%B2G)Wg0l`ubD<|-7 zvAgd0GwVK(Cg1lYd~ZSsA^2<E?{{#{5r*Nq_FnRRA3q2o5Jl0k=r|7gzQ-#o`KwWq zB&ezi#u%>GE5>oe9zqCQE*B6@(~##ms;WZUwh%(>3a@ls2NK8eVdI*|;{l@UIymS5 z61uK~=Xt*~nx=`SX%40H`Mgclw(Ylp06?0ii_|~JH$e~(N-5dn`FxV1C^q|WU3Wft z97hbpu(+*lTPTXMd}~>j=Wxoh#OZW`VHk^<>$-*{NlP(F5=_&C+wBHQ>0B@j165T4 v0J1FG{w~HCEX&&ccABOkiXy10x~Y8v*|O8GhEXa~00000NkvXXu0mjf%rcch diff --git a/Telegram/Resources/icons/player/player_panel_pin@2x.png b/Telegram/Resources/icons/player/player_panel_pin@2x.png deleted file mode 100644 index 07e07315c0feb8b172eef0d655a4d955d63431de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 547 zcmV+;0^I$HP)<h;3K|Lk000e1NJLTq000~S000~a1^@s6at+^<0005%Nkl<ZNQu4H zv8%dJ6bJBc&tH=WQ6xdFMQ|1&XbD^z{WBsV2$#UWz(2u+rUw6zrl8F~p{1cQ!pV&D zhKIc;`Mg@b-+?=b_xSN}J@;Azz^EWx*98D9mrLPXes{R8%Sp^;GtvC_Fx$5IXT)(V zLaW0PFpgt3O|x1s3oBS@^_Za2^ur`cVo8$T!tyGuC2A6qBmn^0?KS{lFc?77G&C9w zbUGa@77LV#`~408n9t|fZnvlbU?ReFI)$pLXf~Tr6a~Fr5B+{0s;UA2>h*dlC@a?M zHU2T@_kHq)m4#(lrPk!t*ladm>qtcW*QjmV>92G+9J25G{{#DO3nHo<&A`%qsA(D> zkH^2no*SQt-bQn<?5%AwnQ$0}FJYeN<!*}~S*^0)?_X}S^ZA^6KNEBu2eK@e#%{M8 zE|*I=kceOyMrkaG!{H#DllowSAb47rFbw&6y$aXwbUJaX)e;?V-xr?eWnx6cL?lFa zyPX(R6onN<VZ$(<stbaE$K!F~mhpW*_bg3Sn1y8}<e{GD2@h}p;MHoyQ510$Ma<0X zI1bCQ%(5*1ie_ztj^h;hMfs{O0h6RW@Hf=5EHP*p#v@@g8WnlN$LMyuY?|gHpNPbH lx~{XX>!q=ul_qGB`JbI(mgk?s23`OF002ovPDHLkV1kf6|3d%( diff --git a/Telegram/Resources/icons/player/player_panel_pin@3x.png b/Telegram/Resources/icons/player/player_panel_pin@3x.png deleted file mode 100644 index 7ad80b27fdd9ef84a8ac534b2b76b19c6267ec0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 825 zcmV-91IGM`P)<h;3K|Lk000e1NJLTq001fg001fo1^@s6#ly*400093Nkl<ZScT1( zPfIdU6vofod0PzSN@o}q8O2&e+X!3~K?_@j6!Z<;1fuu_Y!kF9H%SnE0l}87Dx!_( z2dF^7T?5&!a+C7BD@}8pKex&8fwLLsp83tW_c>=IBEq!7=ksBEdmHof^E~UP0~v`# zP_Nga>pE(+8X}R%?15^UhE}VEVYON<M5EE!0X2f;g!KlJ1J*l8hp^s4I)L>a(kAR| zK-z$v9Y_n<*@85Ioju55*!~0)`n1DhjFBwMq$mohs!FP=k}S&<3WZ2f6jBt0g25mu zibAq1lPHQmQ@EzPyE{syQuI&LG)kpXluRZm91c(A;ZCG!8f|QB5XE9Kyu7^p$=11{ z>+9?O*A}5rD0t0dsH>|hNRsqzCQ%e|dwc6O*NBS6B1BOfcfG2r&~@EwKJ<D$<Z?Ol zhQGD7g>JX&8Dyu^!QS4Uy%BnNc<2dayWK`2kudAoqZhn5uZ>0nOG`^u!)5&9G|y|b zT7@jjcBY{557RiWl}cr}{qTYQ^?5aDKm4GgDB|Yk#&yWo*H@koAoCkmR#sdGy}!SA z49dttnM}s&7RP#geB@d;@hw(YSJCVBT(9WC!GU`fWsFg&R3gSKi|$yiRy3VXyI#q@ zZ1zoSGx_~~yuH2s2_&cEjko^j=!pD&zv(tSKR;8m*>t>pyxUI1VW4uk44==3Kp=o> zwd$y%rBaEjq9dS;G1Tkzi4Xbu`a(XRhad=pHIgKuQmNPm<#GHNsF{IuUB~+R`osxV zEEa8nZf<V=4%Ey*4-XFr27{K#ve~TVvEg+5H|Y8KxnbgylM~F%&Dl=4ySs}{r(>9l z*YN{TBLls^zhi%YpDSr1kwClM9s}i>@q=KDp;oJnBz$^$!t(O+Z{8k@i;HM98Y6kn z&d&IA?(FQ~<KttHtXwW*VPWA{kp1YluWq-C&(BY!(`o*Er>CdTb=|P_xDk>h;rRF% z7Z(?H{ufP-Kp=q2%S)`St+@&~9D*Pq9*;XR<<IyJ{?amWZ&dy{00000NkvXXu0mjf D;PIYI diff --git a/Telegram/Resources/icons/player/player_pause.png b/Telegram/Resources/icons/player/player_pause.png index a09d1730a5b7c374ea4353ba55f9bd9b9cd76148..f7001be9d664a31e1ddd3251f56851bb7049808e 100644 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfU`FpxJhFAzD zCrGd!R$|$fclVb(PXkX1|3jhn6^SlkT#3xQjod~?N+J)0+BMReUN4+rm?iL#L55d( t)1i#p+j0eJGa^~0TO@kBh4NezU|`5?Rpz+FSIY{r!qe5yWt~$(69DS@F**PM delta 198 zcmV;%06G8V0oMVL8Gi-<007{3J@^0s00d`2O+f$vv5yP<VFdsH0FFsSK~zW$?Nvbv z03is}{dm`YTsK#vV67w}I&A||Ow(9H5CQ;k9m8|Xxf-hmAp}S%0TGpwxo{S|dWonM z8?v{CaBaOGJ$kI0c;GI4@);0CHo~J<5seo6%x$pD@J4haT0bVR6x>(zw}^7i!<A=T z6V29oPQ9WSBYXui7tVr5uYNSkXopuf*fFy9zU0<_slh=Kg#Z8m07*qoM6N<$f(=7X Ag#Z8m diff --git a/Telegram/Resources/icons/player/player_pause@2x.png b/Telegram/Resources/icons/player/player_pause@2x.png index ef66eea101f45ac3f94f9ff40e27acc4497fde79..61e268caffe1e810e2239a80632b962f8c6e8d87 100644 GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GvA=6Jd|hD30_ zo$kojpuod&^!~^H>Idwnw0JA$PP=Eqr+y=LmeIFIg&PzY7&#mmnCuk-vUAtQru9A+ zSiC&_bKu;7`VwPl>7)|H+1pFIY+Zg|Y|NheDE|Dj+>e*#?rm7E_*GPaMbXva=NW+( z5yq#+&H_O!Q_k}^dI$#myT<eN-gT>W7Rl?Lc4gnxRs9`3`E0(aBL|Z}0|Se~2SFzD XdhKp6$q8H=K<@Q)^>bP0l+XkK5nNez delta 384 zcmV-`0e}9o0+0ic8Gi-<00374`G)`i00d`2O+f$vv5yP<VFdsH0Y^zhK~!i3?O8z% zgfIxK`fvZ?15Nydc3~qXqOH_4sL7BtT`C=5rhB3ZAw*iAwf3`>uZ5$5rxna9(D!{9 z$1%+F9Ab>S#b^ADWA0ileEt(37J*aEHsjbcjNDm8{A2N)n19%{RZmeIn=AJ0TP^%% z1*dIq{6yL|`@*&h%d&*7>o(u=qZWR$0V38?#}?Bx3}v@k_%@2|b-72P0e8w(LMWgJ zad)ABJLM`N6i|e?yHLQLa+MGYC_>y_DBw=HN(coMA?_{|aHm`)gaV2XcNYq{Q?3$1 z0Y!+r3kBRMS9uAcfFi`*g#zxBtAt4fcxYj@l32kkxs!`cHZV<-<htBTVg;<^PA)dt zz%UGw>vAuN6|j;!SFy<f9ZwQ@7)SH0!@!Tv_}iTcB{lKSSpz>gBNL|Hz9*m1+Y6Wa e(10-U)cyl&uC&4c5B>}Q0000<MNUMnLSTXud#Qy0 diff --git a/Telegram/Resources/icons/player/player_pause@3x.png b/Telegram/Resources/icons/player/player_pause@3x.png index 5392c68d8eb8f6412776b040eb23d93992f33821..8cc5485615ffc5ec37af72de58f67547a0279e50 100644 GIT binary patch literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}CgADf7?Q#I zcBUiOVFMnP$#Q?b?NRREJpUq>(}F&ps3z_#>zz|pWP8qgx%TuSW=0MN1}1?91{MVd zDD%K^jwwOga#wG)yPNF#`xMiu50SM~B7=^Fm7Blf5(|(z`)cu-Nk-1$@69(iT*?TO zx6l5nx-_YrBl)$I`TVQL^*?`DdVfX1#{AD6hCQl^2mB2?IVyR3POg~5VmQsEWe?l2 zln~D*32!B*IOb%dRVt1>s)CB^8x18xCn+48)G^`uff+qoo&w39E+?`R*0^7ddd+=( zyFq~rM{?o=s~<J{Gjr~L-+cIoF^|mgLlF`Z{rh9*eieVc#%{a*AxBKV%ona;oMz9% T+&fiQ5EMq9u6{1-oD!M<ouh|r literal 593 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Ea{HEjtmSN`?>!lvNA9*ad^5o zhEy=Vo#WVd$biQ+IH0^B>fI5`s>Zkl*BLfuTYu#6)={(Zmv;5|;}IIL|FK2?{Y^%( zUYTd7yEPtISP;K`J_`%S8U+U?78ZwT%~_1Ee%r<BO*iRF-gP%GbKdhU>t4^Fo+*~R zaoe$L*W`3xZ=C;gd$?YjY@6ce8)i9cpDw6fRQ0&(-r<dlHgj*ASQ(jWxrjkyU7;<H zhP{@)``vFBi!K~oTy?r?S9)Nj7Jv2pIeyEF92|DufB&+^j$O%eNvf@)!?#PeGT;6_ z&^<k=@3``OJAsRv<_RqL6To`v*@ShQ-+t?#_Vvx?qyIM7)vz%(t@mhP;oxY{VdoSO zV2I=sQcz&nB%|Wsz>s3;(a^wfqG$pGBjbdp3XDul3a1?*o}!6{1yT)bB=g>{n0PpQ z#YF8{%1;&?fBbN3?!4?LAI@e!>3YV&IBVI-?AtciPduD`{e<XQL8g+uaeKF&E$)@s zSJ@l$|77FaTfNdh?bph^n|=QNo%i<Y5zhO+u6e%gjcK`@xbzF(%@W;PO-=uo9?Z&& z^k4D%=Gwo##V=&P^d|f{!}LUjYcqrBfgibyMMABS4O|I-zVWZizfcr1@z@$*8e#Bs L^>bP0l+XkKCM5N9 diff --git a/Telegram/Resources/icons/player/player_play.png b/Telegram/Resources/icons/player/player_play.png index 4dc3dab187ea12e8aafcdd59a02deb26d6359e31..3488f26922960dc32e637b3464ac21f061094d59 100644 GIT binary patch literal 298 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUP4RSb46zV= zJH?UjfPjGOQl+qIea|M%af>;4N;S&&wr8gDuk4d;2lwnb!*T4{0WF1=2|-CaU8jAw zTbk0NS=N}aw{2@!Hm|_BDczgT@4xbHK{D?uTgL^rv^fH^8$Jd01ZswsCaiokZ$k0B zLR<65!zV1iKXlyFG@<+h3vY(KlG9gZ<$x9k#g=#_mQ(c}?+jau4VN)V=l%XA`@CgW PEXbXnu6{1-oD!M<CQw~k delta 346 zcmV-g0j2(`0_p;g8Gi-<007{3J@^0s00d`2O+f$vv5yP<VFdsH0U=35K~zW$wUs>% zML-aMmxxZ{1{xg>pm7K)Dy0kP9K=0zZlThPLiD1Ln789)dA}a(?IfGs?5z1RGiF92 zgaBQ_M1Hhv+crs(glL-fczyYd9xSVA8bE2=7JT1d@0p%+zkeIYk6)!I3NQ@gD(E`^ zfx|FBnx>#A3NL6Mz`Uxe3S8IafgA%MXr5=t^Bi<tKLa`rVBU0H2Voc<g`5X4P+68> zn&vMMKLA1FI6{_XpsMOO&@}+_rmk!7Jns|aKL7+x(*%y=JVBmX1ueEqBFi$7B<an@ zN@VWx?7~7MG*ijKep4*8D2mq5(xC61H|4L`3vCs`nw`*g{r7>>_x+Lv9GO87>?Q7u sZcDOdSrErD(4l|E!0ubDYkVL~Zbac7_X^>Y@&Et;07*qoM6N<$g7YAlRsaA1 diff --git a/Telegram/Resources/icons/player/player_play@2x.png b/Telegram/Resources/icons/player/player_play@2x.png index e4a2e5186b10aea301c16896c7dffefc7f9c4beb..7c283f3312026b4aca9aa462b79839daf9a00038 100644 GIT binary patch delta 422 zcmbQtx|Ml?WIYoD1H+AkybC~zu{g-xiDBJ2nU_G0OKOB?ny)W|7Ld)sz}TM2zyeYN z#8N=az`(qKk%1XVGlIkxFu`RF7BC~&AceQhb9jKt<UL&+Ln1ie&N#^1Y{28HT=n=I z*P262^@-9)bYdFMT5NL27Cp)3+0opR%y({Kk&O5MuM<~=_Fi1dcBMfofLWb+{^h<~ zTXLhXf8F=~O2Wj?g);m9o1QPeY~pG(SyAB`L(}!#+iSL-Ed1xZ<X}>ym}lsThhmdf z#qh147WmY$BAPK&&aosqBy@^GPSdJ=?|<4SA8WbU>Bm_g+WF&!;O<o!S~pWdT+`xt z3s&wrb^49}<&e;;lc(DnTNqv|mF4`RVKnpG8;3`0pUdohF>&wfp!Kg$eHB)HzH^6h zOwtFXU5P1YHJ<4S?4A+rpn4>QX{zvK<|99NyFMlg?mfYjG(m@Dl3=rg0`p9jqY4)c xc-SXwkYH(I`j^x2v`l_W7;}~b?+V5|X8Xz3iZl4{SS|yFny0Iu%Q~loCII$to%R3# delta 645 zcmV;00($+m1Cs@i8Gi-<00374`G)`i00d`2O+f$vv5yP<VFdsH0!m3lK~!i3)!MzT z0YMZ1;9WnJM52%=bcE1oR4SDxP*Le<s5}G}l}<(F2}B{0hysbm6DTAi5{bh9xqHMU z)~>ymyLRSGvROMua=whUXU>o%Ndn~{+iYYbzXxsza5x;o`+sx+p-@PgOeWHPzn4a% zk(5fMgofZfpgIPF0X(-(rvrY!Up4OebDXZ#!DA4o)9E?>+x2d@gL1hHPN!2Oh!&t5 z{X^$`K8Iv7DGK6!0JF4OE%5n#ydj?h5VGBFp;#<}-EQXv`5%DI#%wl&L?XchVGaNx z*XtFU%_exgUVkeg<^kAjY&ILn=ks8**{pz=4`7h-cntA){M(4{0T6PzT%gftfXCzc z7Ua7CHXEze3Uav|$g*rU#2NsD42MIA#bRb6tOX$Cd_F_1Rs*-&Z3e`80Go~FatWDC z=D!Gj00!yz`w)#rKZWoIAmn&FLZwmxm&^4oL<|6%jeo^r0qJ!59fVi_2I+RY5DW(Y z0ud8{5KKuTk%%EecTKFU%7kdj(_1PO3Wl)=4Zz5ii7*7*#^AWegMANqpV#X(gu`J~ zY>#o?U#o-1P7xa3#DPHIx8QHnqZz~#K=x-8A7s^PmF9ztY$}1tTsE84e1&yxRHHH0 z-^7?s*M94Dn&~vz1r6CHCFw;y*|G}R!WQY$BH7|Dme)(A63y~@yWOU_!6Ez>)8TN? z&8W$2^7VQ>4S>`(E0!~+)9DR?XJw<?C>!MZp3K&j%30I!uLerysOcA}!w?`+ps!*P fzO3>paMu3=4Y!K(Tfhwq00000NkvXXu0mjf6Lu^m diff --git a/Telegram/Resources/icons/player/player_play@3x.png b/Telegram/Resources/icons/player/player_play@3x.png index 8a14c8cea52855b868ce4dba34c16c136b71695d..aa280b9a2c84c3e0940a1878938391b29bc9b5df 100644 GIT binary patch literal 609 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}cG}a$F(iZa z?JVoQW&?q?&AWM7b<-P~vJ03eSSZUhHf>hOc)(!evN2otlE$ON1I>qcq`FQRZZp3B zUvN@H<=!U~SX3B29fS~6W4g+R=HnZ3tvQ(Pe?PrR=D6T~g-%hM^V(gHzW-nG+KR_n zDD`3YiA_0+nvSb@${atqzUDJm?rkO^lUd6W*YhPz<GuH4n$3ILo~wJ4bGAmVy4^JM z>*ru|<_q>um-zW_vN`Q`cheVTTb?Vj2P0&iWAA2NJZ}ARmG!v|rju6eT3dAf+vMPK zF$d$gmEDV8JbS<I!>3tJXZ->L)5G@GiE)<x5;9EB4{O(~EngS3+)VbtrvDkq+by0n zta}}E-PGZi*^JL8HK(N>EzJ4Qb>$$>gmp8U_FO7k{=GK8Of%_Ynf76xif3n)eJ@*Y z|7hT}?U~(Y=3i-nAFkc;y1QV0%Zk$nS9I(W=$W=+^??(ehnfXNTUmDhRObv?pE+mm z??7?Z70VAubXaj8bGxEx;J1jc>7#HdOV3gb2I;oM%Kr&ETn9ec%y0ZC#+vY|ygD>g zc=j)5?zy(F^ffN-SZFqB*<Jgu>0(=56ZiD_OhFHHM$ZJ@M*G?G<QOuY59xuT*VEO{ JWt~$(696#v@In9p delta 1310 zcmV+(1>ySP1f~j*8Gi-<0033(vqt~`00d`2O+f$vv5yP<VFdsH1l>tQK~#7F?VL|2 zJYg8eAKE0PNI4J(%Ab;=BnMKQaKHhjxZr>&B3zJ?3m06-0S6pJi5$2flA=&JxHu@J zME-=#Z@$y--FD0Dj@_M^_nq0NwwZnReP`bH^FGh}KF>SvPJajyN%AKsDg;7R(u2wW z2?POoQH3BNFMu3geph}@N=l;g@^XrgkEf%fBU)ZwrmwFrEk!Mn2ZOZgr>d$-dVPJB zT;=leQYtDcQZ3`@dIJ8Y4ks=yPI`HHab@FnzOb+$A&-EK{!5+J0oB#jxdHj@%k%TI z)X~u)IUJ6^+<#Q0yy}4Z`}==m@@v}J*_o7|pD$9wHPzMQEEW<HB3q9($;il{ot+(; zot>rF*jR0a1jywPkT3O@&62LJuBfG@g+fCEetBQ2<X0J8S`ZZ#MH3Sfic652o9kBv z%`U79Al9ht>})zXIH0MiDT;}SVaY-rbp-?x7{?kL8-EqEYiMXtzKcXicR&mVG-!Hy zTG1$ImMCHvAO-|u62g`7@o~af#k&{|2tY8qwYIh@W`|jiXE6m30|I{ueiQsB_)$EH zsel*|*#oDOlM@;p9i_;~NS4gSkqbZ|A;)qg$8;pG6i{__H5YJD32p!}BJ%10EiNw7 z*47rKrhlfgWYdn^0b(TN^(Hz!J*DB{VTy=|V9ACbxdj9gVPRp^+uKVQ7Z=Kkrr~tt z8*&YZ0ZB|uq?MHwT3=tM<m6<QY{*dvK#W95NeP{wpOd^!Md9J$ELqp1P=G)JU<L*T z6i@)Bw+IJ_0fDK(<Y0O*LA^v+Knw^TM_~}hV1JH5J<uXNAOL|I0Cxax0o;Q?iX{Ls zAaE?<V8YRa!x;#%6d(o!p$bA4gf0kS{1Hn60uThf2!0U+BN+BoEDMMMaeW-+%euJb z0WnZmnA_jqS2kd<G%ZKl00NAZloT2o8q&hWna5Uu(CV_XGH2tpoY)S~U!V5c4$#`# zntv9>aptiVAgly*cXvA*ujRxxfad4tsj#q+9v&XFaB=4G&&_mRhTjutO>=WI?e6aS zR7uNfki2z}Iy*ZlD=W)qI3Q|C4O&`SQsS`N+gqQs#<h^80I`i*+uPf&ZS?Eh5+?Tc z_C|eueUzD*Nr8j|vchZ7>gp=Bx3|;H&3}zwnxa-%SU}g;*VNY5MjIO&YHbThx^O1; z@$o@}gM-R$5WV4muuvKVAkZ3Uk5M9o24Q@utgNKc(o#d=pcA+T^!fQoBO@b}o}R8e z|1wHzT<^oJiDA0NW_0*AW(s#21n;+|riO}(i%o}vj^PH-_xCrAjg3)SS{fl#;D1H9 zFfl~hh`14X^CqTh(Ea^A)z{ZkK|ukRaL`$%0Q&j)p~=ZfWe?)q+#GL8<5nNTP3-XS zP%)?D<72mq@cP9t4SIZhq^71O%FD|mQQ)A{bk`vH|C=;3GebQ+Jp@RiiLQX4DVUvL zR-%e7Cie97r2JVWCnrZ}IOx=XYkv@CrLL|n!W3==@dyaxhgR|T@ru^_7@g0fiQV1Z zd96>N9<O-4k%<{p|6q^TPQ1Ur|N0^UB+7dM?fq&BxznVIiV8_ymvMt+r<a@H)e2q* znuLS|s;#Z1=;&zL+uNhf%}v|99!C=(6V2cBTYDU&O|KmX6$k?I(g{I8UKarU1AV54 U4!93Y@&Et;07*qoM6N<$g18K3k^lez diff --git a/Telegram/Resources/icons/player/player_repeat.png b/Telegram/Resources/icons/player/player_repeat.png index 99b52c3ef2db555a51f5885efd009191d440a038..1667941e7e8b1cd2268b314dd7baf7b1206f6334 100644 GIT binary patch literal 396 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUee-m246zV= zJNclXi-Umc%|n5m-6~5InEEy;aPn=Mz*5fnW}j2yv5af)QdgJpty;eOxo`RT^vM6F zeh0q)UF>Po$K+N&NyRmA$GYA9``9<-TK7F(wW{UPGi#gkr$6s`y=(c7TibY>65_AF zYE%+>_P{b@l`7BSse3nW(rLb8a<(%2a_{WTT{D-xytrYijhT^$#*vF2rLl)E%{g5r z(RTD-)4|W1*EO~1^}Lm4=`5J3p|r2+`Hbbw8ZDb9>MNNwEz&r2|NCA;D{Db*ui0AD z_rLfSEYQ_@Xue8g$3>|_OJ!VYCar4wD!EW(S-><^9n;V6{3`zKJ`r#Gorxp$<qNNr Rn+2ed@O1TaS?83{1OS*^m6!kk literal 211 zcmV;^04)EBP)<h;3K|Lk000e1NJLTq000dD000gM1^@s6^naGp0001*Nkl<ZD3P6! zu?oa63`3ur4Bh)Ff&Bl+WG$2u$YgXwNT&;zo<U%PW!aViVj(19W&ms}1ORtO>hnz_ zdl7L=xvp#4bQ}lnP6*-IRkc>pZ<|7rN-4dTr2D>?NKf5bQ_lG(X&|K}QcBB&oU>YM z;;Z}wwN^>e22e_Q_wr%WFz-CiY4g7Cz@O$C{T|$%7~|S{0A|J;!4Lg_nWDnADenLP N002ovPDHLkV1gepSi}GT diff --git a/Telegram/Resources/icons/player/player_repeat@2x.png b/Telegram/Resources/icons/player/player_repeat@2x.png index 870d4ff84b70adce7df6aba6e64f7b5cad007d3e..ae686e152170b3464227c70cbc7bec02971d22f8 100644 GIT binary patch literal 606 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GuFcEZ!eF(iWX zZP>=XW&<A9ZSQnEdj7CPhjun@HO~IuGj%Gj+Yx&MIcepf?W?|*OjL5YEs(tW_ynF4 zD&IbLKR)u!#yo}v1X+|Pq(%y`G&RJmc>Xyu^y<r!M@*hKn+4pimnKShC2lGZQLJfC z^qADrs+zQh({aW!P2XU_i1Xk7-hO-S`(K;$-tr+o>-NX557%3+(KTz?!e9TE{I9S% z_k45X<zsVQa!zg96Qjr0v0m@t61%sh$M+nVsF=Uy@ve1`E!2)W-*%ed`6^W`_-4Z{ zHI=frrb;Y9Ia4+nO|Oyo+0x?8KKVl5qgS)}lPZHMn9iJ1S}pzlf<@n{UNM(F@7Pa% z{_{NF;YNRv!|B7hJb_`~nVT%*?w<%Y*sxzl+H>zMUa6EjPp&@M|96q;G41VlR@;h} z#=XkUPrc+4+{3j&I&g--t;7&_-YlhmdAy;Kw--h24_<g?p`hFI1%iqJYiu_--d}X~ z;`s?@uAj<iycn8kv9F;o|Lx&_@@Ms<66`p88}8L?R=Bio@87gI)7#s0yO^HDP3JKF zqwFlEoL>+$d*jBXM}4xQ%~folZB_rdDKyQlXIq{})tpO?MJ@q$VTGy!<*W3V^qAQD a4(Ok#<vX96cj`GPZarQ7T-G@yGywn`m-a{i literal 235 zcmV<H02Kd;P)<h;3K|Lk000e1NJLTq000^Q000~a1^@s6e-p3M00028Nkl<ZNQu3b zK@P(p5Cd&g@&89`-sv7vP)dl>DtJfAg#(j?#3le?A(BAyv|d(9j<OWdOlA{}GHN#G zVn})?YDmfv0p>euki=`Y_=NGw+kV!GjxTK!jVEIf-A2&1Z5ByH0QwLS!XB_opNNno zAwwcb$dE`9G3leLPWgx1^mb+Jm&h69UF2>H?hbn;p=3yaq?!PL^7J$QmHr=H^~fsv l(YYrp08neS%WM%e@&<SMt}^=}N7?`Y002ovPDHLkV1k6FU+w?^ diff --git a/Telegram/Resources/icons/player/player_repeat@3x.png b/Telegram/Resources/icons/player/player_repeat@3x.png index d37882c23c4760931aa050d662685f04d01088f7..01afab9b3934447503f0b669ca3ab16de2606418 100644 GIT binary patch literal 1011 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbAj}9_#7i7?Q#I zHqv&orlUygD?!arC)X~YJ)In!jr9wD2%Zq&_`}BeRGuSe!|a@?DUW-%d7YXZXeSi1 z@%di8&B3;tvvxf{C$Wlk_3p28s?WcicYE)5JKL50K@OS=L;|=pSf?;~F)lsuSYv{Z z+Uu`X3IY`}UOk?hZ`#a1zkdDthXpt6%=g!FXtC`7Ki~LgO`XxqBT0s{H^?b1GxS`3 zSzccL$Ld}WlRX|hCXYT^T;pA>?$~MjC@`sjFaK7B&AJe+qC0!Md1?d{fB&speRa}f zwf`Yzv)_KLijR-K{Z@?e>`wj{J4<*}ggU=}FMmEmVqzgri<9C{*A1Ui)FwyjO?SKP z*I!~KtNm`H<)QZy*Ro8cT$Kc0o4re3cG^1NHfw%<zKXd}@zmm-bJ9+pZF_u>*>J++ z&G+9+_qv%#nT9*1Y`L3P{9sLYp`gUdogHuH<ZhVcWK?;=<4u8uhx`RKwsky`=d~wo zO%k(Adj0j*xv8(ti%K1v-=lsqX`_MPW_zRg=eb)CJ%7I3WJ<w~>DyBNI)13EtJ`*% zYnzSBn-Z&oiY=uv3~8HZmMU_j6-{K0WxBxaqBV8Z)vS<mr~9neE^VLKd0xFO;Uv#d z75-dTE-QP9q$4{dPL$RM*j^A)Hk0A|YF8Zl*T4K4A6xU?yzlS-^Gm(BC&{S4<M#%o zB=;Bo;w5_(%p3V{yst`n!PQvxMTCiccKWi25Rpr7woT6LcqrH6#Cb|}yQ*`J>`IO& z$2JrP=qa{b+n=yTmqqW}{~3Z(sS~G97Y}ntcq;UG&g`BWx4KkP1=}-D3n#bgOmUs_ z;mnM}8*j^;+Eh9fk2eK%6nE|`+Ii-p!=?u5h3^;rNvzpy=rl?FaFpWb)-`hJOf&7P z)TESFzb;U_`*P8y#XBAEICOQjmX^<Wu~uQdpN|`Vds6$o8QHlZWdWQQtCN=MO4_V( zn0zL3ZP?PFOslz*7R+o`U{f-GcFXCYK-q#LQ8TOSYp=9EXw<RNV(Dn)bL+nEQRG>c z%j<dQZ@!!B@zi^U0xQB+AAcNpWyb8!sT~LRA4~|)5i_6NJ9{73%}FY3(mE&K9rJbX zxRAQZ#!z9x6j!}@0`<3**y01k91B@ix0`8C7qG5~wvb`gn6e<IDB65V>Cy(2R3F5; ZkMZ$NsVhsR!$H}a!PC{xWt~$(69Dpuqs0IK literal 303 zcmV+~0nq-5P)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+0002?Nkl<ZScT2l zOAf;z5Cc$8(}U$^%+a`+yJ=7<-whgDnoR`KlY&_Q0F0QF($2aV9LXJE9Bu;`i(3HW zaTA~ht_$q03QPrz#`N+4-w=$u0{~vJjB(6ta597A?wV(=z-Blcx<Z~=!jy0gOa<44 zDd2{%XxtPQiCe-(xUG$cC#BTlRA3})oD!_Uslf_P5k`_wg?(>~^~Te<QKq=k8Z}(i z8YNuQ8Wmi(H43;%YhrODSY)6IBT1;jNb+T7P|cNdb~D3$-_e#P7XZ&WLlsym?e5)w zkHc5VWa<w+pbV}Dl*0W3<#4~iDBSe%@Oi|G$_MntqzlA+opAsF002ovPDHLkV1fuD BfKLDb diff --git a/Telegram/Resources/icons/player/player_repeat_one.png b/Telegram/Resources/icons/player/player_repeat_one.png deleted file mode 100644 index 8020d6df31cf78f3e45515d2ca74246ec4b55ba7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Z8+Vb&Z8px_Qq z7sn8diM^A2xmpZ(+~VED<rbK0e$k0LrMxS+AXq@~m0F6}qCWeQnhCC#rs)3-V%Xg_ zbMB{I>(=Jp-uv$%V`!?ay+qqXk2n0Yqb{4ItA?r`^XMrIKlPC@VjAmnnRAvdEsHc> z{FW2EDSf=qrr+E*_*zHwk|{bZ6Y_s?zTf+BZ+)#|OM<5A<F6hGpKWH(T>Gvxc6#Y+ zubsMu(J#_|3x|d>DwM_k<!<?EyFx29a-TH6gG*k$?s4%OH;%{7s-Kzw^aF#ZtDnm{ Hr-UW|?xbYO diff --git a/Telegram/Resources/icons/player/player_repeat_one@2x.png b/Telegram/Resources/icons/player/player_repeat_one@2x.png deleted file mode 100644 index cd137b24ee3361c3a6e797632f2ffa0249286e82..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmV-00nh%4P)<h;3K|Lk000e1NJLTq000^Q000~a1^@s6e-p3M00009a7bBm000XU z000XU0RWnu7ytkO;z>k7R7i=vmB9^zFbqXs5MnT<U<YR6W_Dl-4lLvzC~XO-v4fH? z<z!ng--;aowH#G7<@J59%d+U9pO_t#f(^r<v5r`SQ+w^b)^%muHl}I1uk|>xI2*_D zlGpybVaizu!JO}IT@0%N^5_xB)4JAL9Jk2>flNnU2td&2swqb~5O6xmg<L@MJQsn8 z$amyL#Izy2M@04wQ3Il?N)3prYIp}u^6s%a#dCLgb-7D6r4y(W<OC`S0SG&zECisb z0fCzT(bH#a)n|^L{x>OoT36ax1JHF{aagex6zc`cm@~3ytAuL+0000<MNUMnLSTZ{ C$az@+ diff --git a/Telegram/Resources/icons/player/player_repeat_one@3x.png b/Telegram/Resources/icons/player/player_repeat_one@3x.png deleted file mode 100644 index ec01cba5c58d18afe5bd9dc1e488891304fca860..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 377 zcmV-<0fzpGP)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkPD@jB_R9J=0*TId1FboAyXJ%?i9f-!_LMSB1v_LQ1|6F#_WJt0b z6NA6x3xV*2<wy__L0TaM@v}q(?>&}f!M1Jq&DTBh%>jsj-h0FtQOZ!96j;k}oCLV8 zD`Je8=UE@u3-sO>_ff-*XmPeyZT^Y$0x!k&0x!q)0Bg7hDaJW>ZUe2a#>_U`+5;2e zdN-BAF2hM+m*AwZW{tq9z((NIU}tfvuo_PNrT?f9!VzZ$1|Y{-f@L^supMU+1^{Fg z_H$zV{h&6kmL_hW8f&<0YAoRnQ)2~JoEi(b#?;)4tHJIJS%m=rS%m?><Mh9!VxB1_ zcFxf>O?O)kc@hz)l*lS@A8BST_MViFl1AkhN<cHX63`Ux8)y#q3A_vUqDGjxwXB{u Xmg(wRa>R1v00000NkvXXu0mjf7TulJ diff --git a/Telegram/Resources/icons/player/player_repeat_reverse.png b/Telegram/Resources/icons/player/player_repeat_reverse.png deleted file mode 100644 index 6928cf64deca42f1e897d08150df566ff38ef675..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 246 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Z8+Vb&Z8px_Em z7sn8diM^8!ave6{ah~ta_ji87|NqHVXP2@R8QogA<nFDKZifjAZg`Y2m@L}uJ?Tzt zgG%kI^Q^Le?|t1ee}7`y49Q6bn`i%H>frFwoD!7Be`oH!2%&fTX0P4KS6u3Sy~g9x zw%o&u+_zmj$0vNovU|?wlE&Sux0dz&+0?F>y>`+P@dLZoZD0BK(8lE*4wnkv$e!NB tkRX-&oxjJHU(nGj^PF5k&CJL>j7PeTX7~BW$^hNW;OXk;vd$@?2>>rDWs(2@ diff --git a/Telegram/Resources/icons/player/player_repeat_reverse@2x.png b/Telegram/Resources/icons/player/player_repeat_reverse@2x.png deleted file mode 100644 index 6aa3d20bfdedcec0b26fdfa3904c4faf45f37047..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)j!3HGjh1XvOQk(@Ik;M!Q+`=Ht$S`Y;1W@q4 zr;B5VN9WNgu|mxX0?o~rKTeU;pJ!YfutvxGuV%LK;bw&zsZFyPILuvy7z})a4fS=8 zFP*Z=c9)fhR(HzAX;0tm=Kq)w>mYaJP}G7YIj<VmJYTyz<1Bx=g6$Pn7okZT%+>9m zmmJ!7TeRnS**^^x(Gzkvr0)H`pY;D#V(hhj6}gJxD?e-PYX3KRSLHls1r_`4S^Dd* zpI|&FEU=bUlyBmaEqWr8z7+q;;a(e5zUg>e7-LtH$N~=zh9#|n+>8wmIXOP0aCc=b x3X7JF<gTtikQV!8J}-koU$TYH8rI0Yj5eAVqOTr`;Rkw^!PC{xWt~$(69D)+acKYm diff --git a/Telegram/Resources/icons/player/player_repeat_reverse@3x.png b/Telegram/Resources/icons/player/player_repeat_reverse@3x.png deleted file mode 100644 index 2981d37eb7de91c9cf51897d2cb48e68797b61e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 371 zcmV-(0gV2MP)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkPB}qg<R9J=W*g+D5APfc2Pwm0H8S!S^?03=XKqW!#2wC*cumNgc zXH3Kb0Ph$gVy@>g_T2*`X#$7fR@fR1*d6x`vw&T38cYLr!?j>F;1OK-EM7_op*o*m z@|Qi2tFRvV;{ZTJKyn*-@_7*{7A^<MjjZiCThU!{7O>i^&wlZd(Ynsjr^pao4m1x4 zqi`$GT34H}));%{-{-ETid&YYw_}$W<Gu;EZqgnj#`rEKudo8=4YuJt!pQe2z$I+$ zzs(QcjWeE7?O)^$S5%EB+^%Z8;Hp*Q0cTXrSeynM8F+<}B)q~%^5Nmct7S?lNeBV+ zJdd{QG69g361)PplGa{$adRs%MmbRfy1~_eu5jN#ceorl3ipJO;=i&1c>(y1#()`u Rp*sKo002ovPDHLkV1h45oEZQB diff --git a/Telegram/Resources/icons/player/player_repeat_shuffle.png b/Telegram/Resources/icons/player/player_repeat_shuffle.png deleted file mode 100644 index dcb08429b7f807be4ae6cae2a9462eb3d23264fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 269 zcmeAS@N?(olHy`uVBq!ia0vp@K+MO%1|+}KPrC%9I14-?iy0WWg+Z8+Vb&Z8px}N_ z7sn8diBl&X<Z3n$aJetWfA8;<1JC~V|8uwDQfi!Y=*tAHEZtmown@HiC*QC+l$Vt6 z{GF-q`A)A|^uEH@q8`D9LYeQI+f~-F7#N?)5uYJ>*<|Z3m8(&a(^wl4B@B%3)QZd8 zP3lqbw0yqZM|xY1yMpKIUH8l$pPCXBJw3=T_SZ3iCJx0vItArZgN_&;HV-@(Iq3_7 zhvwbhSIWC`^|pqo7@q%p{B-So<}H?gQ<)`n`L~AcTKD^H<%NA`?A!c}FP~=naU-q( P=oJP}S3j3^P6<r_sEBFZ diff --git a/Telegram/Resources/icons/player/player_repeat_shuffle@2x.png b/Telegram/Resources/icons/player/player_repeat_shuffle@2x.png deleted file mode 100644 index 0aa1c6aee9c255045c2c65e2071c988442a1c0c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^Qa~)j!3HGjh1XvOQk(@Ik;M!Q+`=Ht$S`Y;1W@pb zr;B5VN9W#Y-h77@cv#zYivBxWJg<q};?_1v%f!Op>D2MG9CjZrg-I%%L0k;cVk(Ci zxF$>PTzp#jUn(=V(JOUX$G*o5u_Eu!%uY<3q+%?bxT@m)_3aLP(vFXIt1d9^u#abZ zrFe1M$GM$0R_>NZ_TMzPuzSJ2808r=&o-yI345r$D7pGIHG->#DM;Me%w<vqlalA* zcWVp!{%Dt;{kppPJKO3j4Vx5rHH4Yw3!6zZZLno*WUuqndhyO^->O~xzl7HDEtsZf my_q-R{`ZoIDDGEZnKqt1b#?LlXFGuYWAJqKb6Mw<&;$U@>1<#C diff --git a/Telegram/Resources/icons/player/player_repeat_shuffle@3x.png b/Telegram/Resources/icons/player/player_repeat_shuffle@3x.png deleted file mode 100644 index 5fe95448e4d3c76d4feeec8eb1e8fcaad3eca04b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 387 zcmV-}0et?6P)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkPHAzH4R9J=0*jo~VAPfc2P`fa9W|qdzZa-QWk?^oBa5Ix1M(~Iv z)9OS7LJ$DNIhTw*7Fo;y(tV=BiKM{4;Q*Mas>L~~8m}t>@sF&9s%F=#YR{bUxGS&| zhW4tJ=s^caxZxRg23uppaRF3=1zh&}XW&|}^8-xBwP0cArsGO5>$PTUj>O$L{N}p9 z>`HGdxzk3H`;W$b(}3f=k+{;~ZvmT4_%80(G#`iw*WH9?<GP!0?X1ko<2Z603k*Pt zV}d0(Hdw?l!hrX0d`j4-W3(rZ#+9Oqi&evhOR9zmmsJf5&a4^+T%~H(;!3cUA*?U} z5LOrfJgy(?m}2wZt8-5FeP3<KViOVd-U}-*mUQVKkqmAnl}raLAPvp}QsJ&ZI@}q! h3ip&s)V_qzh!<X$y(}q1<iG#`002ovPDHLkV1h(frlbG> diff --git a/Telegram/Resources/icons/player/player_repeat_single.png b/Telegram/Resources/icons/player/player_repeat_single.png new file mode 100644 index 0000000000000000000000000000000000000000..d3d81db044bb40e9174ceed5de7a16a54f54da06 GIT binary patch literal 449 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgfOxx4NF~maf zX^>%`Yk<gKNoUPP6FRywa+>>OGjd&&H*zl8$Se}_<^WfTR-lK7NLt>rzxww-r#(5F zuO``Be%|`~j^no5Z||>fQiz!L^zjb{CX2iKEmte}9(dXEE%1@WG?kO3cV!j13uVOH z67PTSF!!CDIm^K{b=I;)8d_b8GH<mCta!s?k>K>%rvGt9<h88=vyN`d&3TYsy6A%R zHHFhg3a)_(KT?0zowN`xiA|Sy)+0Z`=KN+IYlnE&ZU1s_Pl$Z=^}iiZX#VqGjEWJd zk!D>=hYeosQY&-X6cYM&S6@Xnd*XymItTZ^|Lr2jB6P@<hxy|-KCY$<+ujzZ?6VFJ zZRHGHUmtsYS*FeT!z~{^U6Q)@Va3Ok=KAA>lT;Xk`OjtN-}<`$+gAI#e>we2EY7a? Rj+OxhpQo#z%Q~loCIDP=sK5XK literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_single@2x.png b/Telegram/Resources/icons/player/player_repeat_single@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0bb671ed1353078c642c2b615c3044d7a1511d17 GIT binary patch literal 731 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2Neyz~t)b;usRa z`8Is-Iz~r{)MDk2moElrx)jfMP!v>D)XJJW|CIKNuW^oxFJ!d1s2)-FnBuZ{N=8ey z|GSv^JG0-OdvojTy_DsJWzVdiTYr9gj(`67>^RmvTz^(`u`o5ptuMEb*~cPvNPwrS zH`s-VQN7;DVdKRK3Aq*S3qG{{`O{q5<D=&C=U+STQI_~=o9^bVzM5qr<EJ+H^wXee zd7AU;#Sd)1z4re5b|*!lwO6xNNf^7jdPc9kR<+k|uAkP<yLsVy(>3R3ZHZfdS%A6K z$x&1}S3|D<{PWN0qOTg&PkGJdQ4!+gnd-IFZ@F^Di3ie7xopjjcJr4{2}&@V?YsQ) z$&^ELIt%Q-G|xJ_Y~Sp^b@%hO$9LBJ+Y-Kb)?c<Zsb02=27yo7m|tyVme^x-X2K?` z2W&fecwChPjZ(^UBb1n(pByuMFhSAk66cILiu>1hczpP1b1%p2Wr>wkFJHU!zWIrQ zPwz|QRP`KwUuX5_c}mUsr$rCtIbU<EYA?_^z$I00x1xDR`s?fbes5$il!}{Nk4?NE zm@3KZ8rXGo5zpasHxdjiq<S})yzx<5Bins6Y_(|gibG4>7re=;H82g-ntEvOjWgQ{ zryo9c@CCznyUqJ&`aG+!nIz2+xzuZ^Q_0o&a*>+y@18vWJoCw#=8qLRxlJ59W8PKm zjWT!S3OJp%dC7WC8LPJwrRzI)-F=s|@xm9yTR*3Tx4mb}KefSZ(Y!VFR~F4Y@_3DY z^~ab+6UEHkE}8@tzO%BLQCcp$F6il(roCJhq6N9E`xu{;+%Kul6`co4Se~waF6*2U FngHI{Fuec( literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_repeat_single@3x.png b/Telegram/Resources/icons/player/player_repeat_single@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6aa0c8834c91b18055e70fb4798492aae67c8f3 GIT binary patch literal 1233 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1SD_us|Wxo#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1|Sip>6gA`6MbbHUhz+&y`;uw;_ z`Zmha!`M;eo7kd`Tb*W{D_RvIILzc8T<n<Zq0nnqCe{>H)zCUEtV^kDZ!4ErRL0^3 zD!rMPjx&pVWIFi!|BvZ8ZQEy;-hY=ClNvOm()j(4edgbf%#ryTC>ekRJyI3y?dy}0 zl%#t1?b%cF(SAdduFV1eQ%m{xH9mXrpuzw65vIDDBfK-s4!+pJb+6_^<bTBk5zExX zB=&r_q@BmKI8A3Ly1Kf?u~yaA>gwsK-K%Spxa3!`L!`J^bE3_3xy>p6zJ1%aW5<mf zHzYV%va+($($Y4GoJiiTChWSR>-b||Utg6c&z>z?;MDE%^SDcwwrYJ%&6)G(rPX;F z`uh5oZSmP>n|A5u&7FJp%<)i}H*cQLqT1TTi56B?S$TPCjlNUo&!4}0*RGCbdVKu+ zv!gPn?wWPmDL41+!-or9RW`nS_io*)RbD)r+g?4LI#o1q`ND;cB0G=F(UDubcCEU) z`rEf}lM=d%Dl04F<L9?FU0agRpsK3s?d`pK_3HbxA~ZxIw#+nJxPIHVZCYAdKGAnW z+>|E1e*L<;yL;lqi2)iVRaLK!N#DNqkK@Ym8D|%+c%9A4!zCC~)~=+s{jVTLgoUt- zy!`hsUt(foJ2h0NR5}+XBs3&2eK~pZWN$C;&kLMFN}oEmZrv(az2VR~XRW^qpTds5 zdX>c_RqL8n1C*LNZJN-XI_X9$8#%+fXa2Lg`c%nJ)DDZ2ThCu{%jo9A4~qLPJvcJ? z=!+)>3>PB}7Rz6r)!NGXmh1AxixXcmEtIrsp15t-o;@Oh4nErpy%q_-Hqx`(zJ2>O zs|-`N=e(|)vIQ@`unp!tr#*9)hi%`VsZ*yenbh%Zui)V=9R|h4#V<5!0xsxFW%5?5 za-Prs)=;tbYr4G8|2M+sZVWpX-ZxO&Xj8M>OhPn)HM&AzJIi|ZX`jCTHVTM{=m-o9 z5C8sqL9~LNw)XAYw-1{fSbEvXw@COsTP{oc>gVs?>B-%^c*J(O)ZU3wd)%_#mswj| z2kF;6nCzqWRW-Y+YL-H=@}s{J=c8v8i}aj7nKvi;W?6Z;y^YO_H31){N!&Z;`dnb` zg^H_Y78V`Cvm|arCt0kS_?K7zx6X$>&KsUp0n=G-F0Zzt_N3a>4N}t5%Ip_^|E~Tm zcVm_Uv)7WvPdb2(%GqJY`@v<Y?(3o%6Eq$c+)(q|cI(X>9Zogho})>3_8vJYlf3-M z+=FM&`qtO~fBt;AwfW4og@<n3zd!w&ca*}HPy>lGJf{|mS#}@){o6Y4MTD-w`W?y_ zL<4h<Ec^THE!$fzrE}$cZyXacGC2NsJv<OLt5Z0MVf(&)_io?*{o{uQuZH;q=Om8i zR+jyn9;+wkd075B*U5PQ=FLcNZ|;Am4xZ|I)Fq)bcmDjv_b*gyq!+GEEc_H8dHcYR q9~HH=d)w`%YENZEfsOOe9k92^<W;tr;5QRgoOrtWxvX<aXaWE?3nZWb literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/player/player_shuffle.png b/Telegram/Resources/icons/player/player_shuffle.png index 662958889faad7045dd8bb1f87b9dd59887c0573..1986e7329bde6178de82759dfeb8230eb0a90fa6 100644 GIT binary patch literal 514 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgfEXUKuF~mYJ zIYEMzS&Zr9$B%`Dg%NAdpFaJYpZWd${l{0aDOS9{XS?9hvu9~70zF61o!eLYyKF|_ z|HMNU8Q$Kf`Q_~xLd#1_7ivzNFkwg8Td9p~jHa_^%-B=?oo_|rqa&RKn^RI!^6u_p z6}hvs`1z@++KoIrMC{rc8xOv|zW!m_$<wEu_w1?soVI{{=FFMQOiUkMx=*`&?HXHF zS!ro#SXkYU4~iEql)k>UanmNH)r!;39y+vW*|Kj>P6{v3)DBz2P}F&~bD5vF_v@dZ zpF0GL>BVrEwE8rE`SK-hZxyRa>bEyH9h9G)o$cOty{EVL{=QmnlhjjFG<$k_l9H0P z=ig^5df52({rmoBH99fH#l;>$uin0G_4fAkjNM-+n{lmA)_R(LJYULzr5lWMa&qiy zf3bvIzjm!{8Pkh(r>E;PFI8ah6`RZ5`SRDVsuE5%wgj$Q+j6DP$T;aSrQKSwLc=Gx kL1{^9)Ec)Qt{`<5h8Z94SD(|Lpa%+nPgg&ebxsLQ08jqTFaQ7m literal 346 zcmV-g0j2(lP)<h;3K|Lk000e1NJLTq000dD000gM1^@s6^naGp00009a7bBm000XU z000XU0RWnu7ytkP3`s;mR49>+QZbH$Fc6#tiH?dNxHA%bfV?GN;Vl#hrSVg6$p<KL z<t}73DIz#>ce!IEOS5awtafApK%4+chzI~6IU@i7RR#I-7j6vLosRCmAqgQMgn$?$ zy!SB1z!-yZ91&wg2w|V}W(a`JIg(`4H0-*LB<Y;vc2AP?Ja6@@S=TkEX`;23>$*}^ zSyk1WXyZ5#UM?5*eNU3S-EPN6-_KW3mL*k{s!D6^N6}m4ZQCMA5)8wDqA31Ma^Fc+ zxvnd%wVbBu$4*-ivMdV$AkTATS%$9bAR@S4uejguh%url3aBd5G~LJU@ZQrHLt_lb sapWh+md1a0JRV5X6e5DtqT`2s0vXNm;~r$=IRF3v07*qoM6N<$f?ec_)&Kwi diff --git a/Telegram/Resources/icons/player/player_shuffle@2x.png b/Telegram/Resources/icons/player/player_shuffle@2x.png index 54d44448fa27691cb47ba88316ef7f598f20cf47..3c2751ac87c2764e873f24d1bb91d6ad4a8e7d42 100644 GIT binary patch literal 924 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@H<Xr$#jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zuz(rC1}VI4p2GuF&gSXj7!twx zHr(4gD^O&c&ek*HtZs`sANUKg6e{>Fa<rH%!y?LerPFMM8dswdM-%^)LoG@*E&{wt zO>!cZrH`(By7#_FZ+2_tn~JX-;(g4K>2J60`#Sgio4w!e8O{7MS!4nj{<tr?;r83K z&6P{GN5sYDZH?N`_IK6QR~`X>4^~%KH~jte>(;${@8loXU&}Db&(ClE@#o{m=bRFm z+i&xf*Vo589J@c^>({T-r;AVX=6$HZtn~S(%~7uQ!^}+;F=~@vK7QQ1tk}$dhrxEg z<=G}uA|_(poZeG+-hFrey#Fj~MyD0MoQ0*Ou7&-5eQ9ZFZI?e)?VV9+(tY!=Fl+Cs zt66LK+BlRadYn0T?vrYMUf#DVTd{u8^6Ki-az8h@eb(2}nKN&mQGmqBlpww7_3j^k z)|l{|KAX1r<=0<nn@|4Szx>Z)_mv@H-L6qOlTV&Gdp6Kw|9yYvdGqJ%igAlK_{VEa zJ@wS6SN=Ls<b2xZEl(akRAjz;^X5Y5{{H@mjGPc1=1EH~XRb3~aoGP*i?!MD)U$v8 z?q!&Cxh~=t4}SjS2~a~~Zf>u1my6Prvv=>_J$1?}e%g-yh@=_IS2TXDFDP)hdFRd@ z2_Ck>)1MbMpZ)mJFg1An^^4v=A07SbtWu(U>e;7HMVXnNcUINBnBlfEq-)d45UuX+ z?v8K`k<0&5BL2K^n8LfO+iNM4^q<4?UM*>3*csEe>7eqQ1m@Or=g;q4sHt1c(U9kT z^FrF&604U2Qp&HnYv$MK*s~mPS*W@{KRr4++T_8@mz}cphWEK!4;|UYcOW(<=0eiP zikb&)`!{ZM-2UtO_3+hKfkB<URcgB3G_4q;*2fO*@|`wv{I)!M_wQdEy=l7SbOEbR zU%#3*Hw*T<)w7iDjA8EY_V{mD9>Jhz@+F6LM}UTjPGgUyOkdu1@B1yAJx{!NcJrpB z>z%uIMf(~zv)hUJ&SzNYxBRdGb7Mn+SyF6sfjG0GMt}3l0yCvJ5l1HVM^~?gKFkr& nd=y%xA{Vq_Ndp8*aQ$JM?BBFe)@SxRP|oml^>bP0l+XkKvDcWC literal 537 zcmV+!0_OdRP)<h;3K|Lk000e1NJLTq000^Q000~a1^@s6e-p3M00009a7bBm000XU z000XU0RWnu7ytkP%Sl8*R7i=vma(paFcgMQ@8D7bk%2@923_Fk97$Mo(LrRwiH)bh zmw^yS2vNv?2QP$Ty<&p?Ne4>)e&?K?QWz0|CKLc-j6KZ%ObO}3z5<fLJ_Z`X7-V_& zB&+vukym1jz0N3(W8$25&zy6L<9IZr3koCTI1cFEop2myjO9V2KuMB7RaK~}iX=(i zhY;b1F-F5-+cq^#)1@8+0p)p4o6UxFUGL_brXkz51+Q#>x)RDp6h#1lG)<vt8s_sk zvMduKjiTtmH_W6r<e=4Rh3oYS*L6We*zfnkBDdQO>-G96NV29JL`3j?ABv*DFbot$ zA%Ma#lzN^({s*b53L=8%c><)WDwwAE)b}=sh_F~JP?n_tI-k$)LSBP{AP^vBSz@_d zP75+k6IE3SpwsCjlywqN7={9*C<+*c0Yy>Z`~Eb|(6)D9hr<Cx1lM(^Ywz~Jw#u>$ zx~@ahG^A+?V1ff{Sr+QL7EIgi7DR;IZr9D#bq&k1o`M9}!yAbv`KZzed7ejko>QLZ zbUYr%SOj6reRR28z&Y<qz&S@8$8Tr#wX2-{MLL;=0knaB^e?DijgNs^$ZR&dKhp<F bC)?LwtU||=sK@x800000NkvXXu0mjfVS?*+ diff --git a/Telegram/Resources/icons/player/player_shuffle@3x.png b/Telegram/Resources/icons/player/player_shuffle@3x.png index 14dc3efdd762e46692b73c0c7acabc36e938b5e4..f743ba906285ebdfad55e400bc63f1ea7c02ce8e 100644 GIT binary patch literal 1419 zcmZuxX;2af6b2CiFVJij#UcqWw6e4;tymNjM7(#rMM_gzEXBy9j55@~6iw|QN0}mX zT)d#k*b%SI@+d=E?<J4a)>YFqQ`b!w|Lp#0-hA(S$M@^KH;?D%<7uF4rV9W749Fy6 zfQCuifNE*{umFqD5HLBw(+yD5V?Luvh_NB$vs5Yor-`8eU=9PIy)DteOalM_Bmx3J z8U}9HL|XrKNg~ky@wTww;Hd=wKzoHuBm~8S=ffyAY?{ed<LE*P)<VaI??&blb$0}K zBSkkcJBl4zxjb)dCjex`yNShMFaoRqk=9Hb<*eXovm^7#J&KQImunX)Gd!CWt6|M6 zM_1<*Uw>U4IPjHj8vR}KyMMj0v2k{Gb`*#A_4V!R>%%dghC4Yq+1uGYC@(J;3cETw zl=*XluWM`1yF{DzC-L~%7o`s5goJ%FTpuYCjow;Xa#Qi?^zw^`E~loFLaS?Q2g;7w z+QOQcCXSp+g@u2%S{>P>sHm{=2Y2cvB_+Y(@GC6VsYCatGqbX?#Fj6rtE+`Yy=E{N zjAR3i2dnc@C=|H*SwjN~iR^e5%U~Qkc+jHbR*|&xF)kn+YW8Vvj++pO)3b<6NH}rg zMBee!*OW?>q^io;Y4qK@GchsZ!qD*W5xM*r;mEmwy<zKZi6Z}OIh{r;udlBsMnNDs z?d={X-)?Mdw6}u~A1Dljoc8eWpwVc>X-GpuL!nSub$D!Y@~KqX7R<@Wp!)j4q0pl) zE|}mx<G%v-;PG3_%SBFa#|sOMl9Q8n-ie5e92gi_=O>0d5sMS~O-)S@2*l=5CiMVC zE|;Ux==j~h-@WPvV8Snpi-W_%Ze(qNK)~bi#QcVa)%WjrLzzrw!SX0qCY6p=|G0%{ ziGsN9p5orUdv}b$;c(C>6b1I1vZrUiwKas*O8YzCw30WjZYe<cv3UZ)+s)0BHJIlL zMP7dXrlDu-Ac;zS+%+^lPIq*46fZ3=e+Yhn80qW#iAdDu+Zq*2F*%%O9uJmoibSTe zSeXJSkU2}I(`_muT1sSUwR)1g%f#g3rAyhZd#-hkjYUuCxTopBb0jEtclY%t`(Eh4 z5m;L_+EE`4&k2U2jkHpS{`vNe<BcqSR9IN(?Cgxx*BV69FN?XH*A7-z)1N*W-XPS; zWFXHW0qh3L#KqL_RI~&1h_mz7kUvFkbLL)YsXo<GU#E3=SeTouXY57>mh3kyF;uBk zm1l%{%(S$j-d-a`j$XH>vc8^1B0+s{Kg66p+oVv8?{%xItBbyHL2%X8)wQ;!hV_eP zZeDniOeO<U#)pPNHRp7z>&+VjFZM0dYY!>7KZEq5qO7c~t0fW#93aua^3jAtsjk1j zzxW;|kwCaU#!hB3U$?bovi#@xusBdDqS!%yd;&CpNl8iBynQu}pPLItX={cN!|-(h zF5HU_1f*9a`V`zQD=Qlr8CeW!-gp=q71gs9)%v=n1rR!1)Wiyr<bf3DpJNgarDjZf zFOt6;-rLWeou4mJRT}@?*vbF~yH<?@>u2or`N5Ke;l4ggMFOrLm({&CGh@3FDKb}f z`$>A$y}g8}5b@w0)gPM`wWlc*y%Ng#!T$aVMo`40vJ$mx*Um|-^<y!)LVwNRebGyV zW*ObsZ!`llN#;k?5=fa%%RBGk0cOCG+}&@OQDNQ6K*aGQO1`$C9P{ect9Ts(m6{jP z)SSMxmV>|QULln()uynFhDO@1e7+u$4ohdV*<yuC6#(-(M>P5T!NuIq($Z3MXLUS! zhUjGyyPLn}%5|}xlGsha?OEn=F7}}BSy=2CzHwsOF8QV`e)|!UJ$#5YZqb?l0;mIK AcK`qY literal 761 zcmV<V0tWqwP)<h;3K|Lk000e1NJLTq001Wd001fo1^@s6Fg-%+00009a7bBm000XU z000XU0RWnu7ytkQt4TybR9J=0*RhVWKoAG;Arx9zm{?iTSy<Ei20Va<QZ4N5G%*po z(?V-|3Z6h(OZ5e)3<U{VSYW{T|B8zw+_wvhhkBcABeTOV3$rprMEJxUjYcAIpTPkT z0e%iA0Q@X21NeDd3h-~>m_TL}WBm5Uq9Xy!v+&ih|A2WG4F-c$*kCXy1Q!GIEbx6F zwOWk{tJP}oeZMf=cs$OB1Aygn$;7Qzt72h!A1eSvQG{l*nSeE$O+-;tEG+FPX*3!L z!;qO^v)LqKZQEwXg<*(Bqaj>4KVa$S++wj{Cb29lj?pxYy9@LAT)3(kuykCtT7~C% zi6qzS6^f#Oh)^n(;5ZHw=6N2f)v6GV153vd5&HdpB9UPj@wMS_$lbMGuP1aKroggs zvMl3pH~;`#*F~jL0TH2Iuj6vLFk$=s9<nTF;xb^_I3hx;)dB$Mx*q?wUaz^k)@rpf za|*!3a72XJYz9e^Ktxbg75Dp{2{TPoD4*aI3oMmNAR<VTgza|Agattm7t335kJ)aw zxsP))nG^}jhpSX7I3ABo*ztIbi{&G@$z;OawN9r~G)x?)C<=lgV8XWBZCosmck`_{ z(=?f|`~418RYCM!EYs;!C|_ZH>*JhbjrDpR$LYEbK<u0Y3g}y9S;l_9XTmO*3+nYc zh`x)(bzK0!ZnqQqhY$d!<9fXwch`o)VH{@|M&eqx+ZDoP0Qm`6UnP&>I1b9?GPhXr ze3hgC`MCLf&fPUl)8aVGvY0VnUthvFCXjpglwsR;A_?zH_?hu^-wN=1h}-Q}JTULV z)oR6jOhtBIF)+`9)9IAjx1P`E!f?6!)^~OMPxY-?XCki(pMr@T!mpYFu)l-L-M79A r_jKR-YTU2-)>q;R>04hhMf%o%wds9inqk}V00000NkvXXu0mjfT}ocF diff --git a/Telegram/Resources/icons/player/player_volume0.png b/Telegram/Resources/icons/player/player_volume0.png deleted file mode 100644 index 9a531489baae902915c4a1bb46c5212919aa209c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?58;~rMGjRk`9-c0aAsQ2}UNq!mP~c!bko4z& zcH+8bZo@^5J3KC}+^NUFuy}=*XPa%FL9eaPL(zE!^JCveMb`DWve>0fldAas{H}x4 c@5UAKZc8^MvCLYi3N(|!)78&qol`;+0NK1LzyJUM diff --git a/Telegram/Resources/icons/player/player_volume0@2x.png b/Telegram/Resources/icons/player/player_volume0@2x.png deleted file mode 100644 index dbaec7557b3fbf85db5f17402bc758cceb39bb1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^azHG_!3HE3EPS&CNacCDIEHw1zP)P5b-;j!*<tsW z|N0y5&*EF??J?(yW1gm1hKPAr$BGJVrKf$#`}^At>Pj(v53sOtTDYP0&;v=X#_$0C znrpr_ec@MHg#CWV8yHUx4Q+jR-=;HEhV6WTM$W^6ik9{Rwl+L@AD7?e)|q<dfy@FI QAE3<)p00i_>zopr02$FeaR2}S diff --git a/Telegram/Resources/icons/player/player_volume0@3x.png b/Telegram/Resources/icons/player/player_volume0@3x.png deleted file mode 100644 index 5b7750508315e6b304a3b5f0cf00c155de88593d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 362 zcmV-w0hRuVP)<h;3K|Lk000e1NJLTq001oj001Wl1^@s6n}Nan0003oNkl<ZScUD` zJ+4MT7zN<h4bhQUfW{K0Vh?m;6%w6Ti;_*~#1d#kFAA+hgZCC9k^B1*A2Z3E<g}Bg zoH+vkfbXg-%a|ky?sL)E?*VI?hIyXj*HDW#3<K-B{syKNZJH+beg7kvO0?^`IF945 zVCv9e7~--le}bt&i=v2e9RG`@2CXOxW?A+!_;IwZ>sXfMA@HMULI}2Pdl>u}+O};R zhT#<W5w!37I8D=O@KtDWJ%_D)JPkM#j=n1#eOEa8u5k2S;pn@<(RYPSv>*uZYJ(tv zd7hs(1Hi!xxZ^l%y<D~uLa^&Pw$K3UYrLu|w$K28s;aWJdu){?iLHfc-zbXM+4A=F zJddr_bgN}qZ0*xq4a2~uY1l#otbZ;=QLu#uSbw+DG`$@C37itHDPFDL-T(jq07*qo IM6N<$g8WLR#{d8T diff --git a/Telegram/Resources/icons/player/player_volume1.png b/Telegram/Resources/icons/player/player_volume1.png deleted file mode 100644 index e45f3561220f6e34e3eb7109ad4775f8ed00935e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?58;~rMGjRk`L7py-AsQ3cUfd{jK!Jn#!jaGa z_lrF%d2wiK($ZJ5!8Iui3{!e%dIrsU%ss8JMfFdM@tyZ??q{!-cUL`KvAU9ba!vB2 oj>qqoek(4Q)75yqL*cZ3-RZ4K`kJW$K+_pKUHx3vIVCg!06ID}5dZ)H diff --git a/Telegram/Resources/icons/player/player_volume1@2x.png b/Telegram/Resources/icons/player/player_volume1@2x.png deleted file mode 100644 index 933e8ca9942210ffcf59c213a3149c2c5a12bb17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^azHG_!3HE3EPS&CM0vV6hIn+|z3Rx<puod&VA_xW z;yU?9n~NrJthr%!%-3Q@gYb1XSC><-m;$oI&h2+U{6LJi@qIw~j`m%Ll&|p4Tc~Hy zyzap5LN&XSzuFjf^oI1OT>ZM0VMf^6qbssQ*YX$be0Oxut2xG7&&#BJ?qBuVCAc)& zZ|j`Nx<}ZL?8!3o3{Bl~Lo@cIdA*R%JGS!$;T0{r58K+X>^ls!sJ^O<{mp}EJbt-7 RsX(_fc)I$ztaD0e0syrxTg(6e diff --git a/Telegram/Resources/icons/player/player_volume1@3x.png b/Telegram/Resources/icons/player/player_volume1@3x.png deleted file mode 100644 index 1e9daea63e8f405fea06875ac7561f908ef27bf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 478 zcmV<40U`d0P)<h;3K|Lk000e1NJLTq001oj001Wl1^@s6n}Nan0004{Nkl<ZScUD` zzp8?86bJC{FN`J?dVm%m!Kt1h+r*~`+M>OusIj1DX!#+nZMj6dIJA|}Qk^?^4Y_~S z?EXA(zi>c=&hJCYPdowuy`oyJ7A+PF%BI5p)q~sZHu=6!A5&YjWm&Y}?>~WSi|%&2 zbUYrv1lJPnx-MNVmv6zfLyyN}y5H|Vf@_1;G>zu-`LEd8pbf*I^?IEftQ_6xbZEER z6#^?o+qO+%7#0RALpzQ`aU7QdD?ty3L%LqCrNLFAwWJrXG*m1qr9{8qFW=y)>s|8p zNHd*I%iSU>(L_X!<M2SQR;$N3DP>`_cyIP65kdd}bX~{kbOHdJ&u18hQLK*&Gyr6m z&7RK_CA-J7`A6rCuIrEEIF1YdUI}_On>~)V+imd%Dz?0A*zD73SUnM?l;!?ml@FI^ z(-1-+PmM+++-|o@7Of(=FUw#s;PvK`mTlV<MG-GFki3ltLBI<QfK1cmHTOuX*=+Kf zFlB2pnedtAWy|wCUQ^Swdc7X6`E**AWzpeq;DrW~Z!Vk7h8G%0zHTj-%a@?v0Q61n UuXGEKL;wH)07*qoM6N<$f<YPJV*mgE diff --git a/Telegram/Resources/icons/player/player_volume2.png b/Telegram/Resources/icons/player/player_volume2.png deleted file mode 100644 index 70faf11334c0b1d9ffeed7ab459af70895f73d0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?58;~rMGjRk`iJmTwAsQ3+UUcMZP~dU7Xn*YY ze6eef4`paAoVaU)r}%|EiVO{#^VZ%hvwF@ut*}Ma=1|A+1%G4KPgTiL=4{9nJ;xV$ zZ1XSn`0q6do=4v8UC#J%<GZ~Z?f-w)S<sX0U}n$YdUs!v`lqK`ftE3Ny85}Sb4q9e E08iOF-~a#s diff --git a/Telegram/Resources/icons/player/player_volume2@2x.png b/Telegram/Resources/icons/player/player_volume2@2x.png deleted file mode 100644 index f7a65f5402e01d2d0452948c40d91e128005a150..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226 zcmV<803H8{P)<h;3K|Lk000e1NJLTq0015U000^Y1^@s6prP!v0001~Nkl<ZNQvFl zQ3}K$3<S_|?|+2xtlf`Yp@hUljTSoKM@!68X$635c96VUCg!iB$6$#bfi-kHY@u7> z9;(44*I^HpaI1Ilb=2B%7Y}uvfL%NrmWa8qOjz5Y1(*NJ<&;u@pVWPB`DCE?)LKJz z>2NgfIBJprg2$ctRpY7ONe<`sdg#jZ;3z(b#^4C9)jK_7u!oQI8HRiKcG$v?z#4uG cmiXx0FEA+vNfqZb0{{R307*qoM6N<$f`E2pSpWb4 diff --git a/Telegram/Resources/icons/player/player_volume2@3x.png b/Telegram/Resources/icons/player/player_volume2@3x.png deleted file mode 100644 index 824386b9d9f5f59025ebefe409d3353e19cf7479..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 646 zcmV;10(t$3P)<h;3K|Lk000e1NJLTq001oj001Wl1^@s6n}Nan0006_Nkl<ZScUD` zzpwF77zgmjn_|&QM2ua`rcNXViBT;g{tCG<i52lD7}zkFZI-6NW>SfW#Uvd}Z6zY1 z&pX_=P5tP-xOJT-?~{C!CVhJHA#!@o0RVunh~MwWZnulpa&Ua-!J$wHMN!1dA{HHu zMsc}ZUV({4$K!F_ZnrnVM56QgJnr{<TQG6xa=DDUu73$822D{Ew%hG*vBaPmhQZ-* zcsKa%XqIJBlB9>gcIalaiR1AYtJNxo!{OJUIgZ24X7e!E4!vHl@pL*NBBG)wn9Jpy zMe{t5yWQ?7upL?yMN|~U?9p}o9{7FWVzG$F<MC;*9h#yjOeT}KSS-vwMNu#kiP(F7 zq;LRjBo>REy-6F523cr95HOR;Tw&7bwAm|35?N@Y>h<~=#^>{y_YQ{xnP@~rp6Aa% zxB4&h2i{Qqe&4(<l}Zmk@A>zMex*Df4*&qrG!3e%0sw%fX}~ay*<<Ax{a53>0}Wu0 zy9fQ2pT|$@Kl<LHY1+Kr?RF18{}yzs)iSTA)9K>_{K^D<G4ERInR%M1H)0?VFz;!a zMiyESgsX2DF4i8i*^Dgotvs~K`)VL-K0ZEPvYz(){g_In&cNTUr|(4r0A{mU$9FEB zP6t(0HG3T0xftO%4rN((3@yvDxkpr0)%iW55te0fKA$^+9t;LJnM|-&tG#YFZX_5C zy6fD-h}Y|N*9ns~l}g1uXL;5X3I%tani@$Y67D*mHWH0SakW~x3k~r3pUY@8au*ul g^Yz^8^<IMh3r|quE`fktE&u=k07*qoM6N<$f=vT7IsgCw diff --git a/Telegram/Resources/icons/player/player_volume3.png b/Telegram/Resources/icons/player/player_volume3.png deleted file mode 100644 index 7d27dc27a7f5db0fe32deb9d5631055bce098e9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?58;~rMGjRk`DV{ElAsQ3+PCU!i;K0$6zUX^> zl&ycehEK_4K2GL;A9~+#F}zAin{?&eN47MJtJxcF$y8+<9J0KBzDi6*kyC|{;kL+{ zX+00M{xz;Iw@aARQS|+qLfy5yr+?KvKed3d+Cr-2Gqbhj*<cP6S2>`444$rjF6*2U FngAR`IC}s9 diff --git a/Telegram/Resources/icons/player/player_volume3@2x.png b/Telegram/Resources/icons/player/player_volume3@2x.png deleted file mode 100644 index 501e14e1413bf9496a7ced6c49ec8497d536297c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 242 zcmV<O01f|%P)<h;3K|Lk000e1NJLTq0015U000^Y1^@s6prP!v0002FNkl<ZNQvFn z-425w3<coB-v1HGv$~7TWodfoAI4=(xDX4UMNt9Zs5TKf&M1uUqbpcM_Q4uu@R>*O zKI&CW@ILC@V}kcl3)U4Hcpud;TVB=QM_I)d{>cx`T7VgT8B8m@5?)rQj>o&R9b*jO zHFN0ru><{%IcF$uI~<MwSZWag5M1t^Pc`n^PP#eU>!w$x2S@P;n)*mYaVy`sm>>TU s@bDy>hZ&wkYp{i{^2oUwtnuvFCq`_1GrPv3$^ZZW07*qoM6N<$f|XZk5C8xG diff --git a/Telegram/Resources/icons/player/player_volume3@3x.png b/Telegram/Resources/icons/player/player_volume3@3x.png deleted file mode 100644 index 24ff5175cec95397424e24f12e8ae98142322d9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 875 zcmV-x1C;!UP)<h;3K|Lk000e1NJLTq001oj001Wl1^@s6n}Nan0009rNkl<ZScUD` zJxJ?N6bJCr*P1xk3Ps$kgW%@WNx?yIv<_-;&|Mc5CvjB~bQTpI#45$1lTI$BsGA^K zMZ|&%RysH(El5H8e}nH{8gHw)O=3H|0|!FxM}G;;$2}n;f*Yx-D)#pFP#O--?>smd z3?i9K;%n3`x}&25cXxLJ*b4gS=m?jWmx#q;&@>H-qPUOl?(W9x>uXuC74+NNo2h>~ zoyO46kb7uN(}W+DMc>@q;N#;%7`L*r0<YKWEP8Hk4#i^eTd);$V`C#aJ3BEwJ&kNO zE5IE=`+PoZZ*TvIwTk|F0MTgFG=5}c#2z{j2;lhm*fLla&9W4p$z<5YDL+vt6hbPM zk^)ykudlD;^z;-93kzszX%X5LMZv?v1D|7jd|VzK4u|pb@*)kcgnoQ{<QJc=>lhv$ z7Wzy~Oz=74@wgm15{V$6&sPPmgia=t&~=^nDHe+YyuH1hw?92SSw~MzP2v6hy=rhJ zw9n^5Z*MPdZ*O@&UDwgt+G_fXuqYG?AX^Ft&@8*Ux=c^f<>h5lyJ?<Upc9D%1_lNM zcz=IC@0&`c>I=<cX=zEgQ?Mu$3iXKwfJh`#8CPNdAF4qd931fN+1Xj?@t%K2^ef@< zc!-F|@As2o7(_&r%jGB#2=G3o9M)ghUkkK=klbIBzlYuUkG2|qzn^dC^Lgp<H9>D} zZSn2%^K<zL+`1>13v%c_T~|-Urluy|mdoYp3!O+LgfmQE-aW3auIdY2E~Vb`zMH5t zXJ%$xd7oBQ6@7huCh(8%(^k<$gu%f<$5$@VXcUHF@IH>NTv&#~VWB2r2c6Ak`5MtM z4CmK~EJL9XuCK2hK_4C-;_U1Ui;Ih8SK|#04fa>#ESsB~f2HoRySvNBXEGV6s#-mo zodvflOp2mlV`IZKUeh$zUOk%S{QSHlwLGuai?+5lOioS;*UjqcYH45lXd=Ss=&0?g z>E`BU*;4V^+M4rKQ<gnFJ+|u8&(F`MXF)ohE-gB`gC_d?5^#TiUln?1X9pJ-7l_B> z7#kb=oi70_gTWw9PEMrJ66rje==1TszrXJa`Y#5f7k1OO5+ncs002ovPDHLkV1f!l Bpt=A6 diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 22a726790..ff24886a9 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -29,12 +29,11 @@ mediaPlayerButton: MediaPlayerButton { mediaPlayerButtonSize: size(25px, 30px); mediaPlayerButtonPosition: point(5px, 10px); -mediaPlayerSkipIconPosition: point(5px, 12px); mediaPlayerHeight: 35px; mediaPlayerPadding: 8px; mediaPlayerNameTop: 22px; -mediaPlayerPlayLeft: 7px; +mediaPlayerPlayLeft: 9px; mediaPlayerPlaySkip: 1px; mediaPlayerPlayTop: 0px; mediaPlayerCloseRight: 0px; @@ -47,61 +46,65 @@ mediaPlayerTime: LabelSimple(defaultLabelSimple) { } mediaPlayerRepeatButton: IconButton { - width: 31px; - height: 30px; + width: 30px; + height: 34px; icon: icon { - { "player/player_repeat", mediaPlayerActiveFg, point(9px, 11px) } + { "player/player_repeat", mediaPlayerActiveFg } }; - iconPosition: point(0px, 0px); + iconPosition: point(3px, 6px); - rippleAreaPosition: point(3px, 5px); - rippleAreaSize: 25px; + rippleAreaPosition: point(2px, 5px); + rippleAreaSize: 26px; ripple: RippleAnimation(defaultRippleAnimation) { color: lightButtonBgOver; } } mediaPlayerRepeatDisabledIcon: icon { - { "player/player_repeat", menuIconFg, point(9px, 11px)} + { "player/player_repeat", menuIconFg } }; mediaPlayerRepeatDisabledIconOver: icon { - { "player/player_repeat", menuIconFgOver, point(9px, 11px)} + { "player/player_repeat", menuIconFgOver } }; mediaPlayerRepeatOneIcon: icon { - { "player/player_repeat_one", mediaPlayerActiveFg, point(9px, 11px)} + { "player/player_repeat_single", mediaPlayerActiveFg } }; mediaPlayerRepeatOneDisabledIcon: icon { - { "player/player_repeat_one", menuIconFg, point(9px, 11px)} + { "player/player_repeat_single", menuIconFg } }; mediaPlayerRepeatOneDisabledIconOver: icon { - { "player/player_repeat_one", menuIconFgOver, point(9px, 11px)} + { "player/player_repeat_single", menuIconFgOver } }; mediaPlayerReverseIcon: icon { - { "player/player_reverse", mediaPlayerActiveFg, point(9px, 11px)} + { "player/player_order", mediaPlayerActiveFg } }; mediaPlayerReverseDisabledIcon: icon { - { "player/player_reverse", menuIconFg, point(9px, 11px)} + { "player/player_order", menuIconFg } }; mediaPlayerReverseDisabledIconOver: icon { - { "player/player_reverse", menuIconFgOver, point(9px, 11px)} + { "player/player_order", menuIconFgOver } }; mediaPlayerShuffleIcon: icon { - { "player/player_shuffle", mediaPlayerActiveFg, point(9px, 11px)} -}; -mediaPlayerShuffleDisabledIcon: icon { - { "player/player_shuffle", menuIconFg, point(9px, 11px)} -}; -mediaPlayerShuffleDisabledIconOver: icon { - { "player/player_shuffle", menuIconFgOver, point(9px, 11px)} -}; -mediaPlayerRepeatReverseIcon: icon { - { "player/player_repeat_reverse", mediaPlayerActiveFg, point(9px, 11px)} -}; -mediaPlayerRepeatShuffleIcon: icon { - { "player/player_repeat_shuffle", mediaPlayerActiveFg, point(9px, 11px)} + { "player/player_shuffle", mediaPlayerActiveFg } }; mediaPlayerRepeatDisabledRippleBg: windowBgOver; +mediaPlayerPlayButton: IconButton(mediaPlayerRepeatButton) { + width: 24px; + icon: icon{ + { "player/player_play", mediaPlayerActiveFg } + }; + iconPosition: point(0px, 5px); + rippleAreaPosition: point(0px, 5px); + rippleAreaSize: 24px; +} +mediaPlayerPauseIcon: icon{ + { "player/player_pause", mediaPlayerActiveFg } +}; +mediaPlayerCancelIcon: icon{ + { "player/panel_close", mediaPlayerActiveFg } +}; + mediaPlayerSpeedButton: IconButton { width: 31px; height: 30px; @@ -152,66 +155,52 @@ mediaPlayerPopupMenu: PopupMenu(defaultPopupMenu) { mediaPlayerMenuCheck: icon {{ "player/player_check", mediaPlayerActiveFg }}; mediaPlayerVolumeIcon0: icon { - { "player/player_volume0", mediaPlayerActiveFg }, + { "player/player_mini_off", mediaPlayerActiveFg }, }; mediaPlayerVolumeIcon1: icon { - { "player/player_volume1", mediaPlayerActiveFg }, + { "player/player_mini_half", mediaPlayerActiveFg }, }; -mediaPlayerVolumeIcon2: icon { - { "player/player_volume2", mediaPlayerActiveFg }, -}; -mediaPlayerVolumeIcon3: icon { - { "player/player_volume3", mediaPlayerActiveFg }, -}; -mediaPlayerVolumeToggle: IconButton { - width: 31px; - height: 30px; - - icon: mediaPlayerVolumeIcon0; - iconPosition: point(8px, 11px); - - rippleAreaPosition: point(3px, 5px); - rippleAreaSize: 25px; - ripple: RippleAnimation(defaultRippleAnimation) { - color: lightButtonBgOver; - } +mediaPlayerVolumeToggle: IconButton(mediaPlayerRepeatButton) { + width: 34px; + icon: icon { + { "player/player_mini_full", mediaPlayerActiveFg }, + }; + iconPosition: point(5px, 6px); + rippleAreaPosition: point(4px, 5px); } mediaPlayerVolumeMargin: 10px; mediaPlayerVolumeSize: size(27px, 100px); -mediaPlayerNextButton: IconButton(mediaPlayerRepeatButton) { - width: 25px; +mediaPlayerNextButton: IconButton(mediaPlayerPlayButton) { icon: icon { - { "player/player_next", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, + { "player/player_forward", mediaPlayerActiveFg }, }; - - rippleAreaPosition: point(0px, 5px); } mediaPlayerNextDisabledIcon: icon { - { "player/player_next", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, + { "player/player_forward", mediaPlayerInactiveFg }, }; mediaPlayerPreviousButton: IconButton(mediaPlayerNextButton) { icon: icon { - { "player/player_next-flip_horizontal", mediaPlayerActiveFg, mediaPlayerSkipIconPosition }, + { "player/player_backward", mediaPlayerActiveFg }, }; - rippleAreaPosition: point(1px, 5px); } mediaPlayerPreviousDisabledIcon: icon { - { "player/player_next-flip_horizontal", mediaPlayerInactiveFg, mediaPlayerSkipIconPosition }, + { "player/player_backward", mediaPlayerInactiveFg }, }; -touchBarIconPlayerClose: icon {{ "player/player_close", windowFg }}; +touchBarIconPlayerClose: icon {{ "player/panel_close", windowFg }}; touchBarIconPlayerPlay: icon {{ "media_play", windowFg }}; touchBarIconPlayerPause: icon {{ "media_pause", windowFg }}; -touchBarIconPlayerNext: icon {{ "player/player_next", windowFg }}; -touchBarIconPlayerPrevious: icon {{ "player/player_next-flip_horizontal", windowFg }}; +touchBarIconPlayerNext: icon {{ "player/player_forward", windowFg }}; +touchBarIconPlayerPrevious: icon {{ "player/player_backward", windowFg }}; mediaPlayerClose: IconButton(mediaPlayerRepeatButton) { - width: 37px; - icon: icon {{ "player/player_close", menuIconFg, point(10px, 12px) }}; - iconOver: icon {{ "player/player_close", menuIconFgOver, point(10px, 12px) }}; + width: 39px; + icon: icon {{ "player/panel_close", menuIconFg }}; + iconOver: icon {{ "player/panel_close", menuIconFgOver }}; + iconPosition: point(5px, 6px); - rippleAreaPosition: point(3px, 5px); + rippleAreaPosition: point(4px, 5px); ripple: RippleAnimation(defaultRippleAnimation) { color: windowBgOver; } @@ -245,25 +234,18 @@ mediaPlayerPanelMarginBottom: 10px; mediaPlayerPanelWidth: 344px; mediaPlayerCoverHeight: 102px; -mediaPlayerPanelClose: IconButton(mediaPlayerClose) { - width: 43px; - height: 28px; - icon: icon {{ "player/player_close", menuIconFg, point(16px, 14px) }}; - iconOver: icon {{ "player/player_close", menuIconFgOver, point(16px, 14px) }}; -} - mediaPlayerPanelNextButton: IconButton(mediaPlayerRepeatButton) { width: 37px; - icon: icon {{ "player/player_panel_next", mediaPlayerActiveFg, point(10px, 10px) }}; + icon: icon {{ "player/player_forward", mediaPlayerActiveFg, point(6px, 4px) }}; } mediaPlayerPanelNextDisabledIcon: icon { - { "player/player_panel_next", mediaPlayerInactiveFg, point(10px, 10px) }, + { "player/player_forward", mediaPlayerInactiveFg, point(6px, 4px) }, }; mediaPlayerPanelPreviousButton: IconButton(mediaPlayerPanelNextButton) { - icon: icon {{ "player/player_panel_next-flip_horizontal", mediaPlayerActiveFg, point(10px, 10px) }}; + icon: icon {{ "player/player_backward", mediaPlayerActiveFg, point(6px, 4px) }}; } mediaPlayerPanelPreviousDisabledIcon: icon { - { "player/player_panel_next-flip_horizontal", mediaPlayerInactiveFg, point(10px, 10px) }, + { "player/player_backward", mediaPlayerInactiveFg, point(6px, 4px) }, }; mediaPlayerPanelPadding: 16px; diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index a16b66e6f..35f042f6d 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -497,6 +497,7 @@ bool Instance::moveInPlaylist( const auto jumpById = [&](FullMsgId id) { return jumpByItem(data->history->owner().message(id)); }; + const auto repeatAll = (repeat(data) == RepeatMode::All); if (order(data) == OrderMode::Shuffle) { const auto raw = data->shuffleData.get(); @@ -516,7 +517,7 @@ bool Instance::moveInPlaylist( raw->nonPlayedIds.erase(i); } } - if (repeat(data) == RepeatMode::All) { + if (repeatAll) { ensureShuffleMove(data, delta); } if (raw->nonPlayedIds.empty() @@ -544,7 +545,8 @@ bool Instance::moveInPlaylist( const auto newIndex = *data->playlistIndex + (order(data) == OrderMode::Reverse ? -delta : delta); - const auto useIndex = (!data->playlistSlice + const auto useIndex = (!repeatAll + || !data->playlistSlice || data->playlistSlice->skippedAfter() != 0 || data->playlistSlice->skippedBefore() != 0 || !data->playlistSlice->size()) @@ -553,7 +555,7 @@ bool Instance::moveInPlaylist( % int(data->playlistSlice->size())); if (const auto item = itemByIndex(data, useIndex)) { return jumpByItem(item); - } else if (repeat(data) == RepeatMode::All + } else if (repeatAll && data->playlistOtherSlice && data->playlistOtherSlice->size() > 0) { const auto &other = *data->playlistOtherSlice; diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp index 3eb372d8e..f57baf109 100644 --- a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp +++ b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp @@ -53,8 +53,8 @@ void PrepareRepeatDropdown(not_null<Dropdown*> dropdown) { const auto shuffled = (mode == OrderMode::Shuffle); shuffle->setIconOverride(shuffled ? &st::mediaPlayerShuffleIcon - : &st::mediaPlayerShuffleDisabledIcon, - shuffled ? nullptr : &st::mediaPlayerShuffleDisabledIconOver); + : &st::mediaPlayerShuffleIcon, + shuffled ? nullptr : &st::mediaPlayerShuffleIcon); shuffle->setRippleColorOverride( shuffled ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); const auto reversed = (mode == OrderMode::Reverse); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 2d016c0c0..da0be1820 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" +#include "ui/wrap/fade_wrap.h" #include "ui/effects/ripple_animation.h" #include "ui/text/format_values.h" #include "ui/text/format_song_document_name.h" @@ -236,11 +237,13 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) : RpWidget(parent) , _session(session) , _nameLabel(this, st::mediaPlayerName) -, _timeLabel(this, st::mediaPlayerTime) -, _playPause(this) -, _volumeToggle(this, st::mediaPlayerVolumeToggle) -, _repeatToggle(this, st::mediaPlayerRepeatButton) -, _playbackSpeed(this, st::mediaPlayerSpeedButton) +, _rightControls(this, object_ptr<Ui::RpWidget>(this)) +, _timeLabel(rightControls(), st::mediaPlayerTime) +, _playPause(this, st::mediaPlayerPlayButton) +, _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle) +, _repeatToggle(rightControls(), st::mediaPlayerRepeatButton) +, _orderToggle(rightControls(), st::mediaPlayerRepeatButton) +, _playbackSpeed(rightControls(), st::mediaPlayerSpeedButton) , _close(this, st::mediaPlayerClose) , _shadow(this) , _playbackSlider(this, st::mediaPlayerPlayback) @@ -249,6 +252,8 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) setMouseTracking(true); resize(width(), st::mediaPlayerHeight + st::lineWidth); + _rightControls->show(anim::type::instant); + _nameLabel->setAttribute(Qt::WA_TransparentForMouseEvents); _timeLabel->setAttribute(Qt::WA_TransparentForMouseEvents); @@ -290,13 +295,16 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) updateVolumeToggleIcon(); }, lifetime()); - rpl::combine( - Core::App().settings().playerRepeatModeValue(), - Core::App().settings().playerOrderModeValue() + Core::App().settings().playerRepeatModeValue( ) | rpl::start_with_next([=] { updateRepeatToggleIcon(); }, lifetime()); + Core::App().settings().playerOrderModeValue( + ) | rpl::start_with_next([=] { + updateOrderToggleIcon(); + }, lifetime()); + _repeatToggle->setClickedCallback([=] { auto &settings = Core::App().settings(); settings.setPlayerRepeatMode([&] { @@ -342,23 +350,18 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) }, lifetime()); setType(AudioMsgId::Type::Song); - _playPause->finishTransform(); + //_playPause->finishTransform(); } void Widget::updateVolumeToggleIcon() { - auto icon = []() -> const style::icon * { - auto volume = Core::App().settings().songVolume(); - if (volume > 0) { - if (volume < 1 / 3.) { - return &st::mediaPlayerVolumeIcon1; - } else if (volume < 2 / 3.) { - return &st::mediaPlayerVolumeIcon2; - } - return &st::mediaPlayerVolumeIcon3; - } - return nullptr; - }; - _volumeToggle->setIconOverride(icon()); + _volumeToggle->setIconOverride([] { + const auto volume = Core::App().settings().songVolume(); + return (volume == 0.) + ? &st::mediaPlayerVolumeIcon0 + : (volume < 0.5) + ? &st::mediaPlayerVolumeIcon1 + : nullptr; + }()); } void Widget::setCloseCallback(Fn<void()> callback) { @@ -401,7 +404,7 @@ void Widget::hideShadow() { } QPoint Widget::getPositionForVolumeWidget() const { - auto x = _volumeToggle->x(); + auto x = _volumeToggle->x() + _rightControls->x(); x += (_volumeToggle->width() - st::mediaPlayerVolumeSize.width()) / 2; if (rtl()) x = width() - x - st::mediaPlayerVolumeSize.width(); return QPoint(x, height()); @@ -412,18 +415,22 @@ void Widget::volumeWidgetCreated(Dropdown *widget) { } QPoint Widget::getPositionForRepeatWidget() const { - auto x = _repeatToggle->x(); - x += (_repeatToggle->width() - st::mediaPlayerVolumeSize.width()) / 2; + auto x = _orderToggle->x() + _rightControls->x(); + x += (_orderToggle->width() - st::mediaPlayerVolumeSize.width()) / 2; if (rtl()) x = width() - x - st::mediaPlayerVolumeSize.width(); return QPoint(x, height()); } void Widget::repeatWidgetCreated(Dropdown *widget) { - _repeatToggle->installEventFilter(widget); + _orderToggle->installEventFilter(widget); } Widget::~Widget() = default; +not_null<Ui::RpWidget*> Widget::rightControls() { + return _rightControls->entity(); +} + void Widget::handleSeekProgress(float64 progress) { if (!_lastDurationMs) return; @@ -452,19 +459,29 @@ void Widget::resizeEvent(QResizeEvent *e) { } void Widget::updateControlsGeometry() { - auto right = st::mediaPlayerCloseRight; - _close->moveToRight(right, st::mediaPlayerPlayTop); right += _close->width(); + _close->moveToRight(st::mediaPlayerCloseRight, st::mediaPlayerPlayTop); + auto right = 0; if (hasPlaybackSpeedControl()) { - _playbackSpeed->moveToRight(right, st::mediaPlayerPlayTop); right += _playbackSpeed->width(); + _playbackSpeed->moveToRight(right, 0); right += _playbackSpeed->width(); } - _repeatToggle->moveToRight(right, st::mediaPlayerPlayTop); right += _repeatToggle->width(); - _volumeToggle->moveToRight(right, st::mediaPlayerPlayTop); right += _volumeToggle->width(); + _repeatToggle->moveToRight(right, 0); right += _repeatToggle->width(); + _orderToggle->moveToRight(right, 0); right += _orderToggle->width(); + _volumeToggle->moveToRight(right, 0); right += _volumeToggle->width(); + + updateControlsWrapGeometry(); updatePlayPrevNextPositions(); _playbackSlider->setGeometry(0, height() - st::mediaPlayerPlayback.fullWidth, width(), st::mediaPlayerPlayback.fullWidth); } +void Widget::updateControlsWrapGeometry() { + rightControls()->resize(getTimeRight() + _timeLabel->width(), _repeatToggle->height()); + _rightControls->moveToRight( + st::mediaPlayerCloseRight + _close->width(), + st::mediaPlayerPlayTop); +} + void Widget::paintEvent(QPaintEvent *e) { Painter p(this); auto fill = e->rect().intersected(QRect(0, 0, width(), st::mediaPlayerHeight)); @@ -504,10 +521,10 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) { } void Widget::updateOverLabelsState(QPoint pos) { - auto left = getLabelsLeft(); - auto right = getLabelsRight(); - auto labels = myrtlrect(left, 0, width() - right - left, height() - st::mediaPlayerPlayback.fullWidth); - auto over = labels.contains(pos); + const auto left = getNameLeft(); + const auto right = getNameRight(); + const auto labels = myrtlrect(left, 0, width() - right - left, height() - st::mediaPlayerPlayback.fullWidth); + const auto over = labels.contains(pos); updateOverLabelsState(over); } @@ -531,7 +548,7 @@ void Widget::updatePlayPrevNextPositions() { updateLabelsGeometry(); } -int Widget::getLabelsLeft() const { +int Widget::getNameLeft() const { auto result = st::mediaPlayerPlayLeft + _playPause->width(); if (_previousTrack) { result += _previousTrack->width() + st::mediaPlayerPlaySkip + _nextTrack->width() + st::mediaPlayerPlaySkip; @@ -540,10 +557,18 @@ int Widget::getLabelsLeft() const { return result; } -int Widget::getLabelsRight() const { - auto result = st::mediaPlayerCloseRight + _close->width(); +int Widget::getNameRight() const { + return st::mediaPlayerCloseRight + + _close->width() + + st::mediaPlayerPadding; +} + +int Widget::getTimeRight() const { + auto result = 0; if (_type == AudioMsgId::Type::Song) { - result += _repeatToggle->width() + _volumeToggle->width(); + result += _repeatToggle->width() + + _orderToggle->width() + + _volumeToggle->width(); } if (hasPlaybackSpeedControl()) { result += _playbackSpeed->width(); @@ -553,49 +578,57 @@ int Widget::getLabelsRight() const { } void Widget::updateLabelsGeometry() { - auto left = getLabelsLeft(); - auto right = getLabelsRight(); - - auto widthForName = width() - left - right; - widthForName -= _timeLabel->width() + 2 * st::normalFont->spacew; + const auto left = getNameLeft(); + const auto widthForName = width() + - left + - getNameRight(); _nameLabel->resizeToWidth(widthForName); - _nameLabel->moveToLeft(left, st::mediaPlayerNameTop - st::mediaPlayerName.style.font->ascent); + + const auto right = getTimeRight(); _timeLabel->moveToRight(right, st::mediaPlayerNameTop - st::mediaPlayerTime.font->ascent); + + updateControlsWrapGeometry(); } void Widget::updateRepeatToggleIcon() { - const auto type = AudioMsgId::Type::Song; - const auto repeat = Core::App().settings().playerRepeatMode(); - const auto order = Core::App().settings().playerOrderMode(); - if (repeat == RepeatMode::None && order == OrderMode::Default) { + switch (Core::App().settings().playerRepeatMode()) { + case RepeatMode::None: _repeatToggle->setIconOverride( &st::mediaPlayerRepeatDisabledIcon, &st::mediaPlayerRepeatDisabledIconOver); _repeatToggle->setRippleColorOverride( &st::mediaPlayerRepeatDisabledRippleBg); - return; + break; + case RepeatMode::One: + _repeatToggle->setIconOverride(&st::mediaPlayerRepeatOneIcon); + _repeatToggle->setRippleColorOverride(nullptr); + break; + case RepeatMode::All: + _repeatToggle->setIconOverride(nullptr); + _repeatToggle->setRippleColorOverride(nullptr); + break; + } +} + +void Widget::updateOrderToggleIcon() { + switch (Core::App().settings().playerOrderMode()) { + case OrderMode::Default: + _orderToggle->setIconOverride( + &st::mediaPlayerReverseDisabledIcon, + &st::mediaPlayerReverseDisabledIconOver); + _orderToggle->setRippleColorOverride( + &st::mediaPlayerRepeatDisabledRippleBg); + break; + case OrderMode::Reverse: + _orderToggle->setIconOverride(nullptr); + _orderToggle->setRippleColorOverride(nullptr); + break; + case OrderMode::Shuffle: + _orderToggle->setIconOverride(&st::mediaPlayerShuffleIcon); + _orderToggle->setRippleColorOverride(nullptr); + break; } - const auto &icon = [&]() -> const style::icon& { - switch (repeat) { - case RepeatMode::None: - switch (order) { - case OrderMode::Reverse: return st::mediaPlayerReverseIcon; - case OrderMode::Shuffle: return st::mediaPlayerShuffleIcon; - } - break; - case RepeatMode::One: return st::mediaPlayerRepeatOneIcon; - case RepeatMode::All: - switch (order) { - case OrderMode::Default: return st::mediaPlayerRepeatButton.icon; - case OrderMode::Reverse: return st::mediaPlayerRepeatReverseIcon; - case OrderMode::Shuffle: return st::mediaPlayerRepeatShuffleIcon; - } - break; - } - Unexpected("Repeat / order values in Settings."); - }(); - _repeatToggle->setIconOverride(&icon); } void Widget::checkForTypeChange() { @@ -662,15 +695,11 @@ void Widget::handleSongUpdate(const TrackState &state) { if (instance()->isSeeking(_type)) { showPause = true; } - auto buttonState = [audio = state.id.audio(), showPause] { - if (audio->loading()) { - return ButtonState::Cancel; - } else if (showPause) { - return ButtonState::Pause; - } - return ButtonState::Play; - }; - _playPause->setState(buttonState()); + _playPause->setIconOverride(state.id.audio()->loading() + ? &st::mediaPlayerCancelIcon + : showPause + ? &st::mediaPlayerPauseIcon + : nullptr); updateTimeText(state); } diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index aeb8131f9..36f2f3ae2 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -19,6 +19,8 @@ class LabelSimple; class IconButton; class PlainShadow; class FilledSlider; +template <typename Widget> +class FadeWrap; } // namespace Ui namespace Media { @@ -72,19 +74,24 @@ protected: void mouseReleaseEvent(QMouseEvent *e) override; private: + [[nodiscard]] not_null<Ui::RpWidget*> rightControls(); + void handleSeekProgress(float64 progress); void handleSeekFinished(float64 progress); - int getLabelsLeft() const; - int getLabelsRight() const; + [[nodiscard]] int getNameLeft() const; + [[nodiscard]] int getNameRight() const; + [[nodiscard]] int getTimeRight() const; void updateOverLabelsState(QPoint pos); void updateOverLabelsState(bool over); void updatePlayPrevNextPositions(); void updateLabelsGeometry(); void updateRepeatToggleIcon(); + void updateOrderToggleIcon(); void updateControlsVisibility(); void updateControlsGeometry(); + void updateControlsWrapGeometry(); void createPrevNextButtons(); void destroyPrevNextButtons(); @@ -123,12 +130,14 @@ private: class PlayButton; class SpeedButton; object_ptr<Ui::FlatLabel> _nameLabel; + object_ptr<Ui::FadeWrap<Ui::RpWidget>> _rightControls; object_ptr<Ui::LabelSimple> _timeLabel; object_ptr<Ui::IconButton> _previousTrack = { nullptr }; - object_ptr<PlayButton> _playPause; + object_ptr<Ui::IconButton> _playPause; object_ptr<Ui::IconButton> _nextTrack = { nullptr }; object_ptr<Ui::IconButton> _volumeToggle; object_ptr<Ui::IconButton> _repeatToggle; + object_ptr<Ui::IconButton> _orderToggle; object_ptr<SpeedButton> _playbackSpeed; object_ptr<Ui::IconButton> _close; object_ptr<Ui::PlainShadow> _shadow = { nullptr }; From 4b489ee7d2c07366d360b6f4c0f52c062ce5a62a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 24 Nov 2021 17:44:47 +0400 Subject: [PATCH 082/180] Fade out controls in a narrow player. --- .../media/player/media_player.style | 3 + .../media/player/media_player_widget.cpp | 78 +++++++++++++++++-- .../media/player/media_player_widget.h | 13 +++- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index ff24886a9..5b75be8c7 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -30,6 +30,7 @@ mediaPlayerButtonSize: size(25px, 30px); mediaPlayerButtonPosition: point(5px, 10px); +mediaPlayerWideWidth: 460px; mediaPlayerHeight: 35px; mediaPlayerPadding: 8px; mediaPlayerNameTop: 22px; @@ -171,6 +172,8 @@ mediaPlayerVolumeToggle: IconButton(mediaPlayerRepeatButton) { mediaPlayerVolumeMargin: 10px; mediaPlayerVolumeSize: size(27px, 100px); +mediaPlayerControlsFade: icon {{ "fade_horizontal", mediaPlayerBg }}; + mediaPlayerNextButton: IconButton(mediaPlayerPlayButton) { icon: icon { { "player/player_forward", mediaPlayerActiveFg }, diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index da0be1820..f31fa8ae2 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -252,7 +252,7 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) setMouseTracking(true); resize(width(), st::mediaPlayerHeight + st::lineWidth); - _rightControls->show(anim::type::instant); + setupRightControls(); _nameLabel->setAttribute(Qt::WA_TransparentForMouseEvents); _timeLabel->setAttribute(Qt::WA_TransparentForMouseEvents); @@ -350,7 +350,25 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) }, lifetime()); setType(AudioMsgId::Type::Song); - //_playPause->finishTransform(); +} + +void Widget::setupRightControls() { + const auto raw = rightControls(); + raw->paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + auto p = QPainter(raw); + const auto &icon = st::mediaPlayerControlsFade; + const auto fade = QRect(0, 0, icon.width(), raw->height()); + if (fade.intersects(clip)) { + icon.fill(p, fade); + } + const auto fill = clip.intersected( + { icon.width(), 0, raw->width() - icon.width(), raw->height() }); + if (!fill.isEmpty()) { + p.fillRect(fill, st::mediaPlayerBg); + } + }, raw->lifetime()); + _rightControls->show(anim::type::instant); } void Widget::updateVolumeToggleIcon() { @@ -358,7 +376,7 @@ void Widget::updateVolumeToggleIcon() { const auto volume = Core::App().settings().songVolume(); return (volume == 0.) ? &st::mediaPlayerVolumeIcon0 - : (volume < 0.5) + : (volume < 0.66) ? &st::mediaPlayerVolumeIcon1 : nullptr; }()); @@ -412,6 +430,7 @@ QPoint Widget::getPositionForVolumeWidget() const { void Widget::volumeWidgetCreated(Dropdown *widget) { _volumeToggle->installEventFilter(widget); + widget->installEventFilter(this); } QPoint Widget::getPositionForRepeatWidget() const { @@ -456,6 +475,8 @@ void Widget::handleSeekFinished(float64 progress) { void Widget::resizeEvent(QResizeEvent *e) { updateControlsGeometry(); + _narrow = (width() < st::mediaPlayerWideWidth); + updateControlsWrapVisibility(); } void Widget::updateControlsGeometry() { @@ -476,12 +497,21 @@ void Widget::updateControlsGeometry() { } void Widget::updateControlsWrapGeometry() { - rightControls()->resize(getTimeRight() + _timeLabel->width(), _repeatToggle->height()); + const auto fade = st::mediaPlayerControlsFade.width(); + rightControls()->resize( + getTimeRight() + _timeLabel->width() + fade, + _repeatToggle->height()); _rightControls->moveToRight( st::mediaPlayerCloseRight + _close->width(), st::mediaPlayerPlayTop); } +void Widget::updateControlsWrapVisibility() { + _rightControls->toggle( + _over || !_narrow, + isHidden() ? anim::type::instant : anim::type::normal); +} + void Widget::paintEvent(QPaintEvent *e) { Painter p(this); auto fill = e->rect().intersected(QRect(0, 0, width(), st::mediaPlayerHeight)); @@ -490,8 +520,41 @@ void Widget::paintEvent(QPaintEvent *e) { } } +bool Widget::eventFilter(QObject *o, QEvent *e) { + const auto type = e->type(); + if (type == QEvent::Enter) { + markOver(true); + } else if (type == QEvent::Leave) { + markOver(false); + } + return RpWidget::eventFilter(o, e); +} + +void Widget::enterEventHook(QEnterEvent *e) { + markOver(true); +} + void Widget::leaveEventHook(QEvent *e) { - updateOverLabelsState(false); + markOver(false); +} + +void Widget::markOver(bool over) { + if (over) { + _over = true; + _wontBeOver = false; + updateControlsWrapVisibility(); + } else { + _wontBeOver = true; + InvokeQueued(this, [=] { + if (!_wontBeOver) { + return; + } + _wontBeOver = false; + _over = false; + updateControlsWrapVisibility(); + updateOverLabelsState(false); + }); + } } void Widget::mouseMoveEvent(QMouseEvent *e) { @@ -522,7 +585,10 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) { void Widget::updateOverLabelsState(QPoint pos) { const auto left = getNameLeft(); - const auto right = getNameRight(); + const auto right = width() + - _rightControls->x() + - _rightControls->width() + + getTimeRight(); const auto labels = myrtlrect(left, 0, width() - right - left, height() - st::mediaPlayerPlayback.fullWidth); const auto over = labels.contains(pos); updateOverLabelsState(over); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 36f2f3ae2..876edc7a7 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -41,7 +41,7 @@ class SpeedButton; class Dropdown; struct TrackState; -class Widget : public Ui::RpWidget, private base::Subscriber { +class Widget final : public Ui::RpWidget, private base::Subscriber { public: Widget(QWidget *parent, not_null<Main::Session*> session); @@ -64,17 +64,19 @@ public: ~Widget(); -protected: +private: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; + bool eventFilter(QObject *o, QEvent *e) override; + void enterEventHook(QEnterEvent *e) override; void leaveEventHook(QEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; -private: [[nodiscard]] not_null<Ui::RpWidget*> rightControls(); + void setupRightControls(); void handleSeekProgress(float64 progress); void handleSeekFinished(float64 progress); @@ -92,6 +94,7 @@ private: void updateControlsVisibility(); void updateControlsGeometry(); void updateControlsWrapGeometry(); + void updateControlsWrapVisibility(); void createPrevNextButtons(); void destroyPrevNextButtons(); @@ -106,6 +109,7 @@ private: void updateTimeText(const TrackState &state); void updateTimeLabel(); + void markOver(bool over); const not_null<Main::Session*> _session; @@ -126,6 +130,9 @@ private: bool _labelsOver = false; bool _labelsDown = false; rpl::event_stream<bool> _togglePlaylistRequests; + bool _narrow = false; + bool _over = false; + bool _wontBeOver = false; class PlayButton; class SpeedButton; From cdf09e05193f6283e86c6b0e0da1617534c65921 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 24 Nov 2021 18:02:27 +0400 Subject: [PATCH 083/180] Use composition for playbackSpeed button. --- .../media/player/media_player_widget.cpp | 154 +++++++++--------- .../media/player/media_player_widget.h | 5 +- 2 files changed, 84 insertions(+), 75 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index f31fa8ae2..fea02545b 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -62,72 +62,38 @@ private: }; -class Widget::SpeedButton : public Ui::IconButton { +class Widget::SpeedController final { public: - SpeedButton(QWidget *parent, const style::IconButton &st); + explicit SpeedController(not_null<Ui::IconButton*> button); [[nodiscard]] rpl::producer<> saved() const; -protected: - void contextMenuEvent(QContextMenuEvent *e) override; - private: - class SpeedController final { - public: - SpeedController() { - setSpeed(Core::App().settings().voicePlaybackSpeed()); - _speed = Core::App().settings().voicePlaybackSpeed(true); - } - - [[nodiscard]] rpl::producer<float64> speedValue() const { - return _speedChanged.events_starting_with(speed()); - } - [[nodiscard]] rpl::producer<> saved() const { - return _saved.events(); - } - [[nodiscard]] float64 speed() const { - return _isDefault ? 1. : _speed; - } - [[nodiscard]] bool isDefault() const { - return _isDefault; - } - [[nodiscard]] float64 lastNonDefaultSpeed() const { - return _speed; - } - void toggleDefault() { - _isDefault = !_isDefault; - _speedChanged.fire(speed()); - } - void setSpeed(float64 newSpeed) { - if (!(_isDefault = (newSpeed == 1.))) { - _speed = newSpeed; - } - _speedChanged.fire(speed()); - } - void save() { - Core::App().settings().setVoicePlaybackSpeed(speed()); - Core::App().saveSettingsDelayed(); - _saved.fire({}); - } - - private: - float64 _speed = 2.; - bool _isDefault = true; - rpl::event_stream<float64> _speedChanged; - rpl::event_stream<> _saved; - }; - - SpeedController _speed; + [[nodiscard]] float64 speed() const; + [[nodiscard]] bool isDefault() const; + [[nodiscard]] float64 lastNonDefaultSpeed() const; + void toggleDefault(); + void setSpeed(float64 newSpeed); + void save(); + void showContextMenu(not_null<QContextMenuEvent*> e); + const not_null<Ui::IconButton*> _button; base::unique_qptr<Ui::PopupMenu> _menu; + float64 _speed = 2.; + bool _isDefault = true; + rpl::event_stream<float64> _speedChanged; + rpl::event_stream<> _saved; }; -Widget::SpeedButton::SpeedButton(QWidget *parent, const style::IconButton &st) -: IconButton(parent, st) { - setClickedCallback([=] { - _speed.toggleDefault(); - _speed.save(); +Widget::SpeedController::SpeedController(not_null<Ui::IconButton*> button) +: _button(button) { + setSpeed(Core::App().settings().voicePlaybackSpeed()); + _speed = Core::App().settings().voicePlaybackSpeed(true); + + button->setClickedCallback([=] { + toggleDefault(); + save(); }); struct Icons { @@ -135,10 +101,11 @@ Widget::SpeedButton::SpeedButton(QWidget *parent, const style::IconButton &st) const style::icon *over = nullptr; }; - _speed.speedValue( + _speedChanged.events_starting_with( + speed() ) | rpl::start_with_next([=](float64 speed) { - const auto isDefaultSpeed = _speed.isDefault(); - const auto nonDefaultSpeed = _speed.lastNonDefaultSpeed(); + const auto isDefaultSpeed = isDefault(); + const auto nonDefaultSpeed = lastNonDefaultSpeed(); const auto icons = [&]() -> Icons { if (nonDefaultSpeed == .5) { @@ -171,24 +138,66 @@ Widget::SpeedButton::SpeedButton(QWidget *parent, const style::IconButton &st) } }(); - setIconOverride(icons.icon, icons.over); - setRippleColorOverride(isDefaultSpeed + button->setIconOverride(icons.icon, icons.over); + button->setRippleColorOverride(isDefaultSpeed ? &st::mediaPlayerSpeedDisabledRippleBg : nullptr); - }, lifetime()); + }, button->lifetime()); + + button->events( + ) | rpl::filter([=](not_null<QEvent*> e) { + return (e->type() == QEvent::ContextMenu); + }) | rpl::start_with_next([=](not_null<QEvent*> e) { + showContextMenu(static_cast<QContextMenuEvent*>(e.get())); + }, button->lifetime()); } -void Widget::SpeedButton::contextMenuEvent(QContextMenuEvent *e) { +rpl::producer<> Widget::SpeedController::saved() const { + return _saved.events(); +} + +float64 Widget::SpeedController::speed() const { + return _isDefault ? 1. : _speed; +} + +bool Widget::SpeedController::isDefault() const { + return _isDefault; +} + +float64 Widget::SpeedController::lastNonDefaultSpeed() const { + return _speed; +} + +void Widget::SpeedController::toggleDefault() { + _isDefault = !_isDefault; + _speedChanged.fire(speed()); +} + +void Widget::SpeedController::setSpeed(float64 newSpeed) { + if (!(_isDefault = (newSpeed == 1.))) { + _speed = newSpeed; + } + _speedChanged.fire(speed()); +} + +void Widget::SpeedController::save() { + Core::App().settings().setVoicePlaybackSpeed(speed()); + Core::App().saveSettingsDelayed(); + _saved.fire({}); +} + +void Widget::SpeedController::showContextMenu( + not_null<QContextMenuEvent*> e) { _menu = base::make_unique_q<Ui::PopupMenu>( - this, + _button, st::mediaPlayerPopupMenu); const auto setPlaybackSpeed = [=](float64 speed) { - _speed.setSpeed(speed); - _speed.save(); + setSpeed(speed); + save(); }; - const auto currentSpeed = _speed.speed(); + const auto currentSpeed = speed(); const auto addSpeed = [&](float64 speed, QString text = QString()) { if (text.isEmpty()) { text = QString::number(speed); @@ -206,10 +215,6 @@ void Widget::SpeedButton::contextMenuEvent(QContextMenuEvent *e) { _menu->popup(e->globalPos()); } -rpl::producer<> Widget::SpeedButton::saved() const { - return _speed.saved(); -} - Widget::PlayButton::PlayButton(QWidget *parent) : Ui::RippleButton(parent, st::mediaPlayerButton.ripple) , _layout(st::mediaPlayerButton, [this] { update(); }) { resize(st::mediaPlayerButtonSize); @@ -247,7 +252,10 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) , _close(this, st::mediaPlayerClose) , _shadow(this) , _playbackSlider(this, st::mediaPlayerPlayback) -, _playbackProgress(std::make_unique<View::PlaybackProgress>()) { +, _playbackProgress(std::make_unique<View::PlaybackProgress>()) +, _speedController( + std::make_unique<SpeedController>( + _playbackSpeed.data())) { setAttribute(Qt::WA_OpaquePaintEvent); setMouseTracking(true); resize(width(), st::mediaPlayerHeight + st::lineWidth); @@ -318,7 +326,7 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) Core::App().saveSettingsDelayed(); }); - _playbackSpeed->saved( + _speedController->saved( ) | rpl::start_with_next([=] { instance()->updateVoicePlaybackSpeed(); }, lifetime()); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 876edc7a7..84c022dc2 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -135,7 +135,7 @@ private: bool _wontBeOver = false; class PlayButton; - class SpeedButton; + class SpeedController; object_ptr<Ui::FlatLabel> _nameLabel; object_ptr<Ui::FadeWrap<Ui::RpWidget>> _rightControls; object_ptr<Ui::LabelSimple> _timeLabel; @@ -145,11 +145,12 @@ private: object_ptr<Ui::IconButton> _volumeToggle; object_ptr<Ui::IconButton> _repeatToggle; object_ptr<Ui::IconButton> _orderToggle; - object_ptr<SpeedButton> _playbackSpeed; + object_ptr<Ui::IconButton> _playbackSpeed; object_ptr<Ui::IconButton> _close; object_ptr<Ui::PlainShadow> _shadow = { nullptr }; object_ptr<Ui::FilledSlider> _playbackSlider; std::unique_ptr<View::PlaybackProgress> _playbackProgress; + std::unique_ptr<SpeedController> _speedController; rpl::lifetime _playlistChangesLifetime; From 551e1f787cbfdb1c9df9904423671c710202333c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 24 Nov 2021 18:26:39 +0400 Subject: [PATCH 084/180] Move volume dropdown management to audio player. --- Telegram/CMakeLists.txt | 2 - Telegram/SourceFiles/mainwidget.cpp | 91 +++------------- Telegram/SourceFiles/mainwidget.h | 5 - .../media/player/media_player_dropdown.cpp | 4 +- .../player/media_player_repeat_controls.cpp | 103 ------------------ .../player/media_player_repeat_controls.h | 16 --- .../media/player/media_player_widget.cpp | 60 +++++----- .../media/player/media_player_widget.h | 27 ++--- 8 files changed, 66 insertions(+), 242 deletions(-) delete mode 100644 Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp delete mode 100644 Telegram/SourceFiles/media/player/media_player_repeat_controls.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d0605f9c9..bfd931857 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -813,8 +813,6 @@ PRIVATE media/player/media_player_instance.h media/player/media_player_panel.cpp media/player/media_player_panel.h - media/player/media_player_repeat_controls.cpp - media/player/media_player_repeat_controls.h media/player/media_player_volume_controller.cpp media/player/media_player_volume_controller.h media/player/media_player_widget.cpp diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 50fd731aa..a36fbea4a 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -81,8 +81,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_panel.h" #include "media/player/media_player_widget.h" #include "media/player/media_player_dropdown.h" -#include "media/player/media_player_repeat_controls.h" -#include "media/player/media_player_volume_controller.h" #include "media/player/media_player_instance.h" #include "media/player/media_player_float.h" #include "base/qthelp_regex.h" @@ -801,8 +799,6 @@ void MainWidget::closeBothPlayers() { if (_player) { _player->hide(anim::type::normal); } - _playerVolume.destroyDelayed(); - _playerRepeat.destroyDelayed(); _playerPlaylist->hideIgnoringEnterEvents(); Media::Player::instance()->stop(AudioMsgId::Type::Voice); @@ -821,7 +817,7 @@ void MainWidget::createPlayer() { if (!_player) { _player.create( this, - object_ptr<Media::Player::Widget>(this, &session()), + object_ptr<Media::Player::Widget>(this, this, _controller), _controller->adaptive().oneColumnValue()); rpl::merge( _player->heightValue() | rpl::map_to(true), @@ -849,14 +845,6 @@ void MainWidget::createPlayer() { _playerPlaylist->showFromOther(); }, _player->lifetime()); - _playerVolume.create(this); - Media::Player::PrepareVolumeDropdown( - _playerVolume.data(), - _controller); - _player->entity()->volumeWidgetCreated(_playerVolume); - _playerRepeat.create(this); - Media::Player::PrepareRepeatDropdown(_playerRepeat.data()); - _player->entity()->repeatWidgetCreated(_playerRepeat); orderWidgets(); if (_a_show.animating()) { _player->show(anim::type::instant); @@ -890,8 +878,6 @@ void MainWidget::playerHeightUpdated() { if (!_playerHeight && _player->isHidden()) { const auto state = Media::Player::instance()->getState(Media::Player::instance()->getActiveType()); if (!state.id || Media::Player::IsStoppedOrStopping(state.state)) { - _playerVolume.destroyDelayed(); - _playerRepeat.destroyDelayed(); _player.destroyDelayed(); } } @@ -1539,15 +1525,7 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation( floatPlayerHideAll(); if (_player) { - _player->hideShadow(); - } - auto playerVolumeVisible = _playerVolume && !_playerVolume->isHidden(); - if (playerVolumeVisible) { - _playerVolume->hide(); - } - auto playerRepeatVisible = _playerRepeat && !_playerRepeat->isHidden(); - if (playerRepeatVisible) { - _playerRepeat->hide(); + _player->entity()->hideShadowAndDropdowns(); } auto playerPlaylistVisible = !_playerPlaylist->isHidden(); if (playerPlaylistVisible) { @@ -1573,17 +1551,11 @@ Window::SectionSlideParams MainWidget::prepareShowAnimation( height() - sectionTop)); } - if (playerVolumeVisible) { - _playerVolume->show(); - } - if (playerRepeatVisible) { - _playerRepeat->show(); - } if (playerPlaylistVisible) { _playerPlaylist->show(); } if (_player) { - _player->showShadow(); + _player->entity()->showShadowAndDropdowns(); } floatPlayerShowVisible(); @@ -1848,11 +1820,8 @@ void MainWidget::orderWidgets() { _connecting->raise(); floatPlayerRaiseAll(); _playerPlaylist->raise(); - if (_playerVolume) { - _playerVolume->raise(); - } - if (_playerRepeat) { - _playerRepeat->raise(); + if (_player) { + _player->entity()->raiseDropdowns(); } if (_hider) _hider->raise(); } @@ -1861,15 +1830,7 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m QPixmap result; floatPlayerHideAll(); if (_player) { - _player->hideShadow(); - } - auto playerVolumeVisible = _playerVolume && !_playerVolume->isHidden(); - if (playerVolumeVisible) { - _playerVolume->hide(); - } - auto playerRepeatVisible = _playerRepeat && !_playerRepeat->isHidden(); - if (playerRepeatVisible) { - _playerRepeat->hide(); + _player->entity()->hideShadowAndDropdowns(); } auto playerPlaylistVisible = !_playerPlaylist->isHidden(); if (playerPlaylistVisible) { @@ -1898,17 +1859,11 @@ QPixmap MainWidget::grabForShowAnimation(const Window::SectionSlideParams ¶m _thirdShadow->show(); } } - if (playerVolumeVisible) { - _playerVolume->show(); - } - if (playerRepeatVisible) { - _playerRepeat->show(); - } if (playerPlaylistVisible) { _playerPlaylist->show(); } if (_player) { - _player->showShadow(); + _player->entity()->showShadowAndDropdowns(); } floatPlayerShowVisible(); return result; @@ -2193,7 +2148,9 @@ void MainWidget::updateControlsGeometry() { _mainSection->setGeometryWithTopMoved(mainSectionGeometry, _contentScrollAddToY); } refreshResizeAreas(); - updateMediaPlayerPosition(); + if (_player) { + _player->entity()->updateDropdownsGeometry(); + } updateMediaPlaylistPosition(_playerPlaylist->x()); _contentScrollAddToY = 0; @@ -2392,22 +2349,6 @@ void MainWidget::updateThirdColumnToCurrentChat( } } -void MainWidget::updateMediaPlayerPosition() { - if (!_player) { - return; - } - if (_playerVolume) { - auto relativePosition = _player->entity()->getPositionForVolumeWidget(); - auto playerMargins = _playerVolume->getMargin(); - _playerVolume->moveToLeft(_player->x() + relativePosition.x() - playerMargins.left(), _player->y() + relativePosition.y() - playerMargins.top()); - } - if (_playerRepeat) { - auto relativePosition = _player->entity()->getPositionForRepeatWidget(); - auto playerMargins = _playerRepeat->getMargin(); - _playerRepeat->moveToLeft(_player->x() + relativePosition.x() - playerMargins.left(), _player->y() + relativePosition.y() - playerMargins.top()); - } -} - void MainWidget::updateMediaPlaylistPosition(int x) { if (_player) { auto playlistLeft = x; @@ -2429,12 +2370,10 @@ void MainWidget::returnTabbedSelector() { } } -void MainWidget::keyPressEvent(QKeyEvent *e) { -} - bool MainWidget::eventFilter(QObject *o, QEvent *e) { if (e->type() == QEvent::FocusIn) { - if (const auto widget = qobject_cast<QWidget*>(o)) { + if (o->isWidgetType()) { + const auto widget = static_cast<QWidget*>(o); if (_history == widget || _history->isAncestorOf(widget) || (_mainSection && (_mainSection == widget || _mainSection->isAncestorOf(widget))) || (_thirdSection && (_thirdSection == widget || _thirdSection->isAncestorOf(widget)))) { @@ -2555,10 +2494,8 @@ void MainWidget::searchInChat(Dialogs::Key chat) { } bool MainWidget::contentOverlapped(const QRect &globalRect) { - return (_history->contentOverlapped(globalRect) - || _playerPlaylist->overlaps(globalRect) - || (_playerVolume && _playerVolume->overlaps(globalRect))) - || (_playerRepeat && _playerRepeat->overlaps(globalRect)); + return _history->contentOverlapped(globalRect) + || _playerPlaylist->overlaps(globalRect); } void MainWidget::activate() { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 87ce1a6e4..98c1d877f 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -55,7 +55,6 @@ class Widget; namespace Media { namespace Player { class Widget; -class Dropdown; class Panel; struct TrackState; } // namespace Player @@ -241,7 +240,6 @@ public Q_SLOTS: protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; bool eventFilter(QObject *o, QEvent *e) override; private: @@ -249,7 +247,6 @@ private: void handleAdaptiveLayoutUpdate(); void updateWindowAdaptiveLayout(); void handleAudioUpdate(const Media::Player::TrackState &state); - void updateMediaPlayerPosition(); void updateMediaPlaylistPosition(int x); void updateControlsGeometry(); void updateDialogsWidthAnimated(); @@ -366,8 +363,6 @@ private: object_ptr<Window::TopBarWrapWidget<Media::Player::Widget>> _player = { nullptr }; - object_ptr<Media::Player::Dropdown> _playerVolume = { nullptr }; - object_ptr<Media::Player::Dropdown> _playerRepeat = { nullptr }; object_ptr<Media::Player::Panel> _playerPlaylist; bool _playerUsingPanel = false; diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp index 855c8e46e..64e4adac5 100644 --- a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp @@ -33,10 +33,12 @@ Dropdown::Dropdown(QWidget *parent) } QMargins Dropdown::getMargin() const { - const auto top = st::mediaPlayerHeight + const auto top1 = st::mediaPlayerHeight + st::lineWidth - st::mediaPlayerPlayTop - st::mediaPlayerVolumeToggle.height; + const auto top2 = st::mediaPlayerPlayback.fullWidth; + const auto top = std::max(top1, top2); return QMargins(st::mediaPlayerVolumeMargin, top, st::mediaPlayerVolumeMargin, st::mediaPlayerVolumeMargin); } diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp b/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp deleted file mode 100644 index f57baf109..000000000 --- a/Telegram/SourceFiles/media/player/media_player_repeat_controls.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "media/player/media_player_repeat_controls.h" - -#include "media/player/media_player_dropdown.h" -#include "media/player/media_player_instance.h" -#include "ui/widgets/buttons.h" -#include "core/core_settings.h" -#include "core/application.h" -#include "styles/style_media_player.h" - -namespace Media::Player { - -void PrepareRepeatDropdown(not_null<Dropdown*> dropdown) { - const auto makeButton = [&] { - const auto result = Ui::CreateChild<Ui::IconButton>( - dropdown.get(), - st::mediaPlayerRepeatButton); - result->show(); - return result; - }; - - const auto repeatOne = makeButton(); - const auto repeatAll = makeButton(); - const auto shuffle = makeButton(); - const auto reverse = makeButton(); - - Core::App().settings().playerRepeatModeValue( - ) | rpl::start_with_next([=](RepeatMode mode) { - const auto one = (mode == RepeatMode::One); - repeatOne->setIconOverride(one - ? &st::mediaPlayerRepeatOneIcon - : &st::mediaPlayerRepeatOneDisabledIcon, - one ? nullptr : &st::mediaPlayerRepeatOneDisabledIconOver); - repeatOne->setRippleColorOverride( - one ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); - const auto all = (mode == RepeatMode::All); - repeatAll->setIconOverride(all - ? nullptr - : &st::mediaPlayerRepeatDisabledIcon, - all ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); - repeatAll->setRippleColorOverride( - all ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); - }, dropdown->lifetime()); - - Core::App().settings().playerOrderModeValue( - ) | rpl::start_with_next([=](OrderMode mode) { - const auto shuffled = (mode == OrderMode::Shuffle); - shuffle->setIconOverride(shuffled - ? &st::mediaPlayerShuffleIcon - : &st::mediaPlayerShuffleIcon, - shuffled ? nullptr : &st::mediaPlayerShuffleIcon); - shuffle->setRippleColorOverride( - shuffled ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); - const auto reversed = (mode == OrderMode::Reverse); - reverse->setIconOverride(reversed - ? &st::mediaPlayerReverseIcon - : &st::mediaPlayerReverseDisabledIcon, - reversed ? nullptr : &st::mediaPlayerReverseDisabledIconOver); - reverse->setRippleColorOverride( - reversed ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); - }, dropdown->lifetime()); - - const auto toggleRepeat = [](RepeatMode mode) { - auto &settings = Core::App().settings(); - const auto active = (settings.playerRepeatMode() == mode); - settings.setPlayerRepeatMode(active ? RepeatMode::None : mode); - Core::App().saveSettingsDelayed(); - }; - const auto toggleOrder = [](OrderMode mode) { - auto &settings = Core::App().settings(); - const auto active = (settings.playerOrderMode() == mode); - settings.setPlayerOrderMode(active ? OrderMode::Default : mode); - Core::App().saveSettingsDelayed(); - }; - repeatOne->setClickedCallback([=] { toggleRepeat(RepeatMode::One); }); - repeatAll->setClickedCallback([=] { toggleRepeat(RepeatMode::All); }); - shuffle->setClickedCallback([=] { toggleOrder(OrderMode::Shuffle); }); - reverse->setClickedCallback([=] { toggleOrder(OrderMode::Reverse); }); - - dropdown->sizeValue( - ) | rpl::start_with_next([=](QSize size) { - const auto rect = QRect(QPoint(), size); - const auto inner = rect.marginsRemoved(dropdown->getMargin()); - const auto skip = (inner.height() - repeatOne->height() * 4) / 3; - auto top = 0; - const auto move = [&](auto &widget) { - widget->move((size.width() - widget->width()) / 2, top); - top += widget->height() + skip; - }; - move(repeatOne); - move(repeatAll); - move(shuffle); - move(reverse); - }, dropdown->lifetime()); -} - -} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_repeat_controls.h b/Telegram/SourceFiles/media/player/media_player_repeat_controls.h deleted file mode 100644 index 17cac6868..000000000 --- a/Telegram/SourceFiles/media/player/media_player_repeat_controls.h +++ /dev/null @@ -1,16 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -namespace Media::Player { - -class Dropdown; - -void PrepareRepeatDropdown(not_null<Dropdown*> dropdown); - -} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index fea02545b..476a0ccf5 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_button.h" #include "media/player/media_player_instance.h" #include "media/player/media_player_dropdown.h" +#include "media/player/media_player_volume_controller.h" #include "styles/style_media_player.h" #include "styles/style_media_view.h" #include "history/history_item.h" @@ -238,9 +239,12 @@ QPoint Widget::PlayButton::prepareRippleStartPosition() const { return QPoint(mapFromGlobal(QCursor::pos()) - st::mediaPlayerButton.rippleAreaPosition); } -Widget::Widget(QWidget *parent, not_null<Main::Session*> session) +Widget::Widget( + QWidget *parent, + not_null<Ui::RpWidget*> dropdownsParent, + not_null<Window::SessionController*> controller) : RpWidget(parent) -, _session(session) +, _controller(controller) , _nameLabel(this, st::mediaPlayerName) , _rightControls(this, object_ptr<Ui::RpWidget>(this)) , _timeLabel(rightControls(), st::mediaPlayerTime) @@ -252,6 +256,7 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) , _close(this, st::mediaPlayerClose) , _shadow(this) , _playbackSlider(this, st::mediaPlayerPlayback) +, _volume(dropdownsParent.get()) , _playbackProgress(std::make_unique<View::PlaybackProgress>()) , _speedController( std::make_unique<SpeedController>( @@ -357,6 +362,10 @@ Widget::Widget(QWidget *parent, not_null<Main::Session*> session) handleSongUpdate(state); }, lifetime()); + PrepareVolumeDropdown(_volume.data(), controller); + _volumeToggle->installEventFilter(_volume.data()); + _volume->installEventFilter(this); + setType(AudioMsgId::Type::Song); } @@ -419,37 +428,36 @@ void Widget::setShadowGeometryToLeft(int x, int y, int w, int h) { _shadow->setGeometryToLeft(x, y, w, h); } -void Widget::showShadow() { +void Widget::showShadowAndDropdowns() { _shadow->show(); _playbackSlider->setVisible(_type == AudioMsgId::Type::Song); + if (_volumeHidden) { + _volumeHidden = false; + _volume->show(); + } } -void Widget::hideShadow() { +void Widget::updateDropdownsGeometry() { + const auto position = _volume->parentWidget()->mapFromGlobal( + _volumeToggle->mapToGlobal( + QPoint( + (_volumeToggle->width() - st::mediaPlayerVolumeSize.width()) / 2, + height()))); + const auto playerMargins = _volume->getMargin(); + _volume->move(position - QPoint(playerMargins.left(), playerMargins.top())); +} + +void Widget::hideShadowAndDropdowns() { _shadow->hide(); _playbackSlider->hide(); + if (!_volume->isHidden()) { + _volumeHidden = true; + _volume->hide(); + } } -QPoint Widget::getPositionForVolumeWidget() const { - auto x = _volumeToggle->x() + _rightControls->x(); - x += (_volumeToggle->width() - st::mediaPlayerVolumeSize.width()) / 2; - if (rtl()) x = width() - x - st::mediaPlayerVolumeSize.width(); - return QPoint(x, height()); -} - -void Widget::volumeWidgetCreated(Dropdown *widget) { - _volumeToggle->installEventFilter(widget); - widget->installEventFilter(this); -} - -QPoint Widget::getPositionForRepeatWidget() const { - auto x = _orderToggle->x() + _rightControls->x(); - x += (_orderToggle->width() - st::mediaPlayerVolumeSize.width()) / 2; - if (rtl()) x = width() - x - st::mediaPlayerVolumeSize.width(); - return QPoint(x, height()); -} - -void Widget::repeatWidgetCreated(Dropdown *widget) { - _orderToggle->installEventFilter(widget); +void Widget::raiseDropdowns() { + _volume->raise(); } Widget::~Widget() = default; @@ -502,6 +510,8 @@ void Widget::updateControlsGeometry() { updatePlayPrevNextPositions(); _playbackSlider->setGeometry(0, height() - st::mediaPlayerPlayback.fullWidth, width(), st::mediaPlayerPlayback.fullWidth); + + updateDropdownsGeometry(); } void Widget::updateControlsWrapGeometry() { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 84c022dc2..968144386 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -29,9 +29,9 @@ class PlaybackProgress; } // namespace Clip } // namespace Media -namespace Main { -class Session; -} // namespace Main +namespace Window { +class SessionController; +} // namespace Window namespace Media { namespace Player { @@ -43,20 +43,19 @@ struct TrackState; class Widget final : public Ui::RpWidget, private base::Subscriber { public: - Widget(QWidget *parent, not_null<Main::Session*> session); + Widget( + QWidget *parent, + not_null<Ui::RpWidget*> dropdownsParent, + not_null<Window::SessionController*> controller); void setCloseCallback(Fn<void()> callback); void setShowItemCallback(Fn<void(not_null<const HistoryItem*>)> callback); void stopAndClose(); void setShadowGeometryToLeft(int x, int y, int w, int h); - void showShadow(); - void hideShadow(); - - [[nodiscard]] QPoint getPositionForVolumeWidget() const; - void volumeWidgetCreated(Dropdown *widget); - - [[nodiscard]] QPoint getPositionForRepeatWidget() const; - void repeatWidgetCreated(Dropdown *widget); + void hideShadowAndDropdowns(); + void showShadowAndDropdowns(); + void updateDropdownsGeometry(); + void raiseDropdowns(); [[nodiscard]] rpl::producer<bool> togglePlaylistRequests() const { return _togglePlaylistRequests.events(); @@ -111,7 +110,7 @@ private: void updateTimeLabel(); void markOver(bool over); - const not_null<Main::Session*> _session; + const not_null<Window::SessionController*> _controller; crl::time _seekPositionMs = -1; crl::time _lastDurationMs = 0; @@ -133,6 +132,7 @@ private: bool _narrow = false; bool _over = false; bool _wontBeOver = false; + bool _volumeHidden = false; class PlayButton; class SpeedController; @@ -149,6 +149,7 @@ private: object_ptr<Ui::IconButton> _close; object_ptr<Ui::PlainShadow> _shadow = { nullptr }; object_ptr<Ui::FilledSlider> _playbackSlider; + object_ptr<Dropdown> _volume; std::unique_ptr<View::PlaybackProgress> _playbackProgress; std::unique_ptr<SpeedController> _speedController; From d6a30c48536e9102074a2d75c551fd55ce925335 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 19 Nov 2021 09:51:02 +0300 Subject: [PATCH 085/180] Slightly refactored AdminLog::GenerateItems. --- .../admin_log/history_admin_log_inner.cpp | 62 +- .../admin_log/history_admin_log_item.cpp | 634 ++++++++++++------ 2 files changed, 464 insertions(+), 232 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 9b4a0638b..405f015ba 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -788,37 +788,39 @@ void InnerWidget::addEvents(Direction direction, const QVector<MTPChannelAdminLo : newItemsForDownDirection; addToItems.reserve(oldItemsCount + events.size() * 2); for (const auto &event : events) { - event.match([&](const MTPDchannelAdminLogEvent &data) { - const auto id = data.vid().v; - if (_eventIds.find(id) != _eventIds.end()) { - return; - } - - auto count = 0; - const auto addOne = [&](OwnedItem item, TimeId sentDate) { - if (sentDate) { - _itemDates.emplace(item->data(), sentDate); - } - _eventIds.emplace(id); - _itemsByData.emplace(item->data(), item.get()); - addToItems.push_back(std::move(item)); - ++count; - }; - GenerateItems( - this, - _history, - data, - addOne); - if (count > 1) { - // Reverse the inner order of the added messages, because we load events - // from bottom to top but inside one event they go from top to bottom. - auto full = addToItems.size(); - auto from = full - count; - for (auto i = 0, toReverse = count / 2; i != toReverse; ++i) { - std::swap(addToItems[from + i], addToItems[full - i - 1]); - } - } + const auto &data = event.match([](const MTPDchannelAdminLogEvent &d) + -> const MTPDchannelAdminLogEvent & { + return d; }); + const auto id = data.vid().v; + if (_eventIds.find(id) != _eventIds.end()) { + return; + } + + auto count = 0; + const auto addOne = [&](OwnedItem item, TimeId sentDate) { + if (sentDate) { + _itemDates.emplace(item->data(), sentDate); + } + _eventIds.emplace(id); + _itemsByData.emplace(item->data(), item.get()); + addToItems.push_back(std::move(item)); + ++count; + }; + GenerateItems( + this, + _history, + data, + addOne); + if (count > 1) { + // Reverse the inner order of the added messages, because we load events + // from bottom to top but inside one event they go from top to bottom. + auto full = addToItems.size(); + auto from = full - count; + for (auto i = 0, toReverse = count / 2; i != toReverse; ++i) { + std::swap(addToItems[from + i], addToItems[full - i - 1]); + } + } } auto newItemsCount = _items.size() + ((direction == Direction::Up) ? 0 : newItemsForDownDirection.size()); if (newItemsCount != oldItemsCount) { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index b98f177bd..3d21438f5 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -23,14 +23,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/sticker_set_box.h" #include "base/unixtime.h" #include "core/application.h" -#include "mainwindow.h" // App::wnd()->sessionController +#include "core/click_handler_types.h" #include "main/main_session.h" +#include "window/window_session_controller.h" #include "facades.h" namespace AdminLog { namespace { -TextWithEntities PrepareText(const QString &value, const QString &emptyValue) { +TextWithEntities PrepareText( + const QString &value, + const QString &emptyValue) { auto result = TextWithEntities { TextUtilities::Clean(value) }; if (result.text.isEmpty()) { result.text = emptyValue; @@ -41,7 +44,12 @@ TextWithEntities PrepareText(const QString &value, const QString &emptyValue) { int(emptyValue.size()) }); } } else { - TextUtilities::ParseEntities(result, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands); + TextUtilities::ParseEntities( + result, + TextParseLinks + | TextParseMentions + | TextParseHashtags + | TextParseBotCommands); } return result; } @@ -122,7 +130,8 @@ bool MediaCanHaveCaption(const MTPMessage &message) { const auto &data = message.c_message(); const auto media = data.vmedia(); const auto mediaType = media ? media->type() : mtpc_messageMediaEmpty; - return (mediaType == mtpc_messageMediaDocument || mediaType == mtpc_messageMediaPhoto); + return (mediaType == mtpc_messageMediaDocument) + || (mediaType == mtpc_messageMediaPhoto); } TextWithEntities ExtractEditedText( @@ -138,7 +147,10 @@ TextWithEntities ExtractEditedText( }; } -const auto CollectChanges = [](auto &phraseMap, auto plusFlags, auto minusFlags) { +const auto CollectChanges = []( + auto &phraseMap, + auto plusFlags, + auto minusFlags) { auto withPrefix = [&phraseMap](auto flags, QChar prefix) { auto result = QString(); for (auto &phrase : phraseMap) { @@ -149,7 +161,8 @@ const auto CollectChanges = [](auto &phraseMap, auto plusFlags, auto minusFlags) return result; }; const auto kMinus = QChar(0x2212); - return withPrefix(plusFlags & ~minusFlags, '+') + withPrefix(minusFlags & ~plusFlags, kMinus); + return withPrefix(plusFlags & ~minusFlags, '+') + + withPrefix(minusFlags & ~plusFlags, kMinus); }; TextWithEntities GenerateAdminChangeText( @@ -160,7 +173,11 @@ TextWithEntities GenerateAdminChangeText( using Flag = ChatAdminRight; using Flags = ChatAdminRights; - auto result = tr::lng_admin_log_promoted(tr::now, lt_user, user, Ui::Text::WithEntities); + auto result = tr::lng_admin_log_promoted( + tr::now, + lt_user, + user, + Ui::Text::WithEntities); const auto useInviteLinkPhrase = channel->isMegagroup() && channel->anyoneCanAddMembers(); @@ -190,7 +207,10 @@ TextWithEntities GenerateAdminChangeText( prevRights.flags &= ~Flag::BanUsers; } - auto changes = CollectChanges(phraseMap, newRights.flags, prevRights.flags); + const auto changes = CollectChanges( + phraseMap, + newRights.flags, + prevRights.flags); if (!changes.isEmpty()) { result.text.append('\n' + changes); } @@ -228,16 +248,26 @@ TextWithEntities GenerateBannedChangeText( ChatRestrictionsInfo prevRights) { using Flag = ChatRestriction; - auto newFlags = newRights.flags; - auto newUntil = newRights.until; - auto prevFlags = prevRights.flags; - auto indefinitely = ChannelData::IsRestrictedForever(newUntil); + const auto newFlags = newRights.flags; + const auto newUntil = newRights.until; + const auto prevFlags = prevRights.flags; + const auto indefinitely = ChannelData::IsRestrictedForever(newUntil); if (newFlags & Flag::ViewMessages) { - return tr::lng_admin_log_banned(tr::now, lt_user, user, Ui::Text::WithEntities); - } else if (newFlags == 0 && (prevFlags & Flag::ViewMessages) && !peerIsUser(participantId)) { - return tr::lng_admin_log_unbanned(tr::now, lt_user, user, Ui::Text::WithEntities); + return tr::lng_admin_log_banned( + tr::now, + lt_user, + user, + Ui::Text::WithEntities); + } else if (newFlags == 0 + && (prevFlags & Flag::ViewMessages) + && !peerIsUser(participantId)) { + return tr::lng_admin_log_unbanned( + tr::now, + lt_user, + user, + Ui::Text::WithEntities); } - auto untilText = indefinitely + const auto untilText = indefinitely ? tr::lng_admin_log_restricted_forever(tr::now) : tr::lng_admin_log_restricted_until( tr::now, @@ -306,7 +336,11 @@ TextWithEntities GenerateInviteLinkChangeText( int(link.text.size()), InternalInviteLinkUrl(newLink) }); } - auto result = tr::lng_admin_log_edited_invite_link(tr::now, lt_link, link, Ui::Text::WithEntities); + auto result = tr::lng_admin_log_edited_invite_link( + tr::now, + lt_link, + link, + Ui::Text::WithEntities); result.text.append('\n'); const auto label = [](const MTPExportedChatInvite &link) { @@ -348,19 +382,41 @@ TextWithEntities GenerateInviteLinkChangeText( const auto wasRequestApproval = requestApproval(prevLink); const auto nowRequestApproval = requestApproval(newLink); if (wasLabel != nowLabel) { - result.text.append('\n').append(tr::lng_admin_log_invite_link_label(tr::now, lt_previous, wasLabel, lt_limit, nowLabel)); + result.text.append('\n').append( + tr::lng_admin_log_invite_link_label( + tr::now, + lt_previous, + wasLabel, + lt_limit, + nowLabel)); } if (wasExpireDate != nowExpireDate) { - result.text.append('\n').append(tr::lng_admin_log_invite_link_expire_date(tr::now, lt_previous, wrapDate(wasExpireDate), lt_limit, wrapDate(nowExpireDate))); + result.text.append('\n').append( + tr::lng_admin_log_invite_link_expire_date( + tr::now, + lt_previous, + wrapDate(wasExpireDate), + lt_limit, + wrapDate(nowExpireDate))); } if (wasUsageLimit != nowUsageLimit) { - result.text.append('\n').append(tr::lng_admin_log_invite_link_usage_limit(tr::now, lt_previous, wrapUsage(wasUsageLimit), lt_limit, wrapUsage(nowUsageLimit))); + result.text.append('\n').append( + tr::lng_admin_log_invite_link_usage_limit( + tr::now, + lt_previous, + wrapUsage(wasUsageLimit), + lt_limit, + wrapUsage(nowUsageLimit))); } if (wasRequestApproval != nowRequestApproval) { - result.text.append('\n').append(nowRequestApproval ? tr::lng_admin_log_invite_link_request_needed(tr::now) : tr::lng_admin_log_invite_link_request_not_needed(tr::now)); + result.text.append('\n').append( + nowRequestApproval + ? tr::lng_admin_log_invite_link_request_needed(tr::now) + : tr::lng_admin_log_invite_link_request_not_needed(tr::now)); } - result.entities.push_front(EntityInText(EntityType::Italic, 0, result.text.size())); + result.entities.push_front( + EntityInText(EntityType::Italic, 0, result.text.size())); return result; }; @@ -368,7 +424,7 @@ auto GenerateParticipantString( not_null<Main::Session*> session, PeerId participantId) { // User name in "User name (@username)" format with entities. - auto peer = session->data().peer(participantId); + const auto peer = session->data().peer(participantId); auto name = TextWithEntities { peer->name }; if (const auto user = peer->asUser()) { auto entityData = QString::number(user->id.value) @@ -380,7 +436,7 @@ auto GenerateParticipantString( int(name.text.size()), entityData }); } - auto username = peer->userName(); + const auto username = peer->userName(); if (username.isEmpty()) { return name; } @@ -413,16 +469,24 @@ auto GenerateParticipantChangeTextInner( user, ChatAdminRightsInfo(), ChatAdminRightsInfo( - oldParticipant->c_channelParticipantAdmin().vadmin_rights())); + oldParticipant + ->c_channelParticipantAdmin() + .vadmin_rights())); } else if (oldType == mtpc_channelParticipantBanned) { return GenerateBannedChangeText( participantId, user, ChatRestrictionsInfo(), ChatRestrictionsInfo( - oldParticipant->c_channelParticipantBanned().vbanned_rights())); + oldParticipant + ->c_channelParticipantBanned() + .vbanned_rights())); } - return tr::lng_admin_log_invited(tr::now, lt_user, user, Ui::Text::WithEntities); + return tr::lng_admin_log_invited( + tr::now, + lt_user, + user, + Ui::Text::WithEntities); }; return participant.match([&](const MTPDchannelParticipantCreator &data) { // No valid string here :( @@ -443,7 +507,9 @@ auto GenerateParticipantChangeTextInner( ChatAdminRightsInfo(data.vadmin_rights()), (oldType == mtpc_channelParticipantAdmin ? ChatAdminRightsInfo( - oldParticipant->c_channelParticipantAdmin().vadmin_rights()) + oldParticipant + ->c_channelParticipantAdmin() + .vadmin_rights()) : ChatAdminRightsInfo())); }, [&](const MTPDchannelParticipantBanned &data) { const auto participantId = peerFromMTP(data.vpeer()); @@ -456,7 +522,9 @@ auto GenerateParticipantChangeTextInner( ChatRestrictionsInfo(data.vbanned_rights()), (oldType == mtpc_channelParticipantBanned ? ChatRestrictionsInfo( - oldParticipant->c_channelParticipantBanned().vbanned_rights()) + oldParticipant + ->c_channelParticipantBanned() + .vbanned_rights()) : ChatRestrictionsInfo())); }, [&](const MTPDchannelParticipantLeft &data) { return generateOther(peerFromMTP(data.vpeer())); @@ -465,19 +533,32 @@ auto GenerateParticipantChangeTextInner( }); } -TextWithEntities GenerateParticipantChangeText(not_null<ChannelData*> channel, const MTPChannelParticipant &participant, const MTPChannelParticipant *oldParticipant = nullptr) { - auto result = GenerateParticipantChangeTextInner(channel, participant, oldParticipant); - result.entities.push_front(EntityInText(EntityType::Italic, 0, result.text.size())); +TextWithEntities GenerateParticipantChangeText( + not_null<ChannelData*> channel, + const MTPChannelParticipant &participant, + const MTPChannelParticipant *oldParticipant = nullptr) { + auto result = GenerateParticipantChangeTextInner( + channel, + participant, + oldParticipant); + result.entities.push_front( + EntityInText(EntityType::Italic, 0, result.text.size())); return result; } -TextWithEntities GenerateDefaultBannedRightsChangeText(not_null<ChannelData*> channel, ChatRestrictionsInfo rights, ChatRestrictionsInfo oldRights) { - auto result = TextWithEntities{ tr::lng_admin_log_changed_default_permissions(tr::now) }; +TextWithEntities GenerateDefaultBannedRightsChangeText( + not_null<ChannelData*> channel, + ChatRestrictionsInfo rights, + ChatRestrictionsInfo oldRights) { + auto result = TextWithEntities{ + tr::lng_admin_log_changed_default_permissions(tr::now) + }; const auto changes = GenerateBannedChangeText(rights, oldRights); if (!changes.isEmpty()) { result.text.append('\n' + changes); } - result.entities.push_front(EntityInText(EntityType::Italic, 0, result.text.size())); + result.entities.push_front( + EntityInText(EntityType::Italic, 0, result.text.size())); return result; } @@ -527,6 +608,48 @@ void GenerateItems( Fn<void(OwnedItem item, TimeId sentDate)> callback) { Expects(history->peer->isChannel()); + using LogTitle = MTPDchannelAdminLogEventActionChangeTitle; + using LogAbout = MTPDchannelAdminLogEventActionChangeAbout; + using LogUsername = MTPDchannelAdminLogEventActionChangeUsername; + using LogPhoto = MTPDchannelAdminLogEventActionChangePhoto; + using LogInvites = MTPDchannelAdminLogEventActionToggleInvites; + using LogSign = MTPDchannelAdminLogEventActionToggleSignatures; + using LogPin = MTPDchannelAdminLogEventActionUpdatePinned; + using LogEdit = MTPDchannelAdminLogEventActionEditMessage; + using LogDelete = MTPDchannelAdminLogEventActionDeleteMessage; + using LogJoin = MTPDchannelAdminLogEventActionParticipantJoin; + using LogLeave = MTPDchannelAdminLogEventActionParticipantLeave; + using LogInvite = MTPDchannelAdminLogEventActionParticipantInvite; + using LogBan = MTPDchannelAdminLogEventActionParticipantToggleBan; + using LogPromote = MTPDchannelAdminLogEventActionParticipantToggleAdmin; + using LogSticker = MTPDchannelAdminLogEventActionChangeStickerSet; + using LogPreHistory = + MTPDchannelAdminLogEventActionTogglePreHistoryHidden; + using LogPermissions = MTPDchannelAdminLogEventActionDefaultBannedRights; + using LogPoll = MTPDchannelAdminLogEventActionStopPoll; + using LogDiscussion = MTPDchannelAdminLogEventActionChangeLinkedChat; + using LogLocation = MTPDchannelAdminLogEventActionChangeLocation; + using LogSlowMode = MTPDchannelAdminLogEventActionToggleSlowMode; + using LogStartCall = MTPDchannelAdminLogEventActionStartGroupCall; + using LogDiscardCall = MTPDchannelAdminLogEventActionDiscardGroupCall; + using LogMute = MTPDchannelAdminLogEventActionParticipantMute; + using LogUnmute = MTPDchannelAdminLogEventActionParticipantUnmute; + using LogCallSetting = + MTPDchannelAdminLogEventActionToggleGroupCallSetting; + using LogJoinByInvite = + MTPDchannelAdminLogEventActionParticipantJoinByInvite; + using LogInviteDelete = + MTPDchannelAdminLogEventActionExportedInviteDelete; + using LogInviteRevoke = + MTPDchannelAdminLogEventActionExportedInviteRevoke; + using LogInviteEdit = MTPDchannelAdminLogEventActionExportedInviteEdit; + using LogVolume = MTPDchannelAdminLogEventActionParticipantVolume; + using LogTTL = MTPDchannelAdminLogEventActionChangeHistoryTTL; + using LogJoinByRequest = + MTPDchannelAdminLogEventActionParticipantJoinByRequest; + using LogNoForwards = MTPDchannelAdminLogEventActionToggleNoForwards; + using LogActionSendMessage = MTPDchannelAdminLogEventActionSendMessage; + const auto session = &history->session(); const auto id = event.vid().v; const auto from = history->owner().user(event.vuser_id().v); @@ -544,7 +667,9 @@ void GenerateItems( const auto fromLink = from->createOpenLink(); const auto fromLinkText = textcmdLink(1, fromName); - auto addSimpleServiceMessage = [&](const QString &text, PhotoData *photo = nullptr) { + const auto addSimpleServiceMessage = [&]( + const QString &text, + PhotoData *photo = nullptr) { auto message = HistoryService::PreparedText { text }; message.links.push_back(fromLink); addPart(history->makeServiceMessage( @@ -556,7 +681,7 @@ void GenerateItems( photo)); }; - auto createChangeTitle = [&](const MTPDchannelAdminLogEventActionChangeTitle &action) { + const auto createChangeTitle = [&](const LogTitle &action) { auto text = (channel->isMegagroup() ? tr::lng_action_changed_title : tr::lng_admin_log_changed_title_channel)( @@ -568,11 +693,12 @@ void GenerateItems( addSimpleServiceMessage(text); }; - auto makeSimpleTextMessage = [&](TextWithEntities &&text) { - auto bodyFlags = MessageFlag::HasFromId | MessageFlag::AdminLogEntry; - auto bodyReplyTo = MsgId(); - auto bodyViaBotId = UserId(); - auto bodyGroupedId = uint64(); + const auto makeSimpleTextMessage = [&](TextWithEntities &&text) { + const auto bodyFlags = MessageFlag::HasFromId + | MessageFlag::AdminLogEntry; + const auto bodyReplyTo = MsgId(); + const auto bodyViaBotId = UserId(); + const auto bodyGroupedId = uint64(); return history->makeMessage( history->nextNonHistoryEntryId(), bodyFlags, @@ -587,14 +713,14 @@ void GenerateItems( bodyGroupedId); }; - auto addSimpleTextMessage = [&](TextWithEntities &&text) { + const auto addSimpleTextMessage = [&](TextWithEntities &&text) { addPart(makeSimpleTextMessage(std::move(text))); }; - auto createChangeAbout = [&](const MTPDchannelAdminLogEventActionChangeAbout &action) { - auto newValue = qs(action.vnew_value()); - auto oldValue = qs(action.vprev_value()); - auto text = (channel->isMegagroup() + const auto createChangeAbout = [&](const LogAbout &action) { + const auto newValue = qs(action.vnew_value()); + const auto oldValue = qs(action.vprev_value()); + const auto text = (channel->isMegagroup() ? (newValue.isEmpty() ? tr::lng_admin_log_removed_description_group : tr::lng_admin_log_changed_description_group) @@ -604,19 +730,22 @@ void GenerateItems( )(tr::now, lt_from, fromLinkText); addSimpleServiceMessage(text); - auto newDescription = PrepareText(newValue, QString()); - auto body = makeSimpleTextMessage(PrepareText(newValue, QString())); + const auto body = makeSimpleTextMessage( + PrepareText(newValue, QString())); if (!oldValue.isEmpty()) { - auto oldDescription = PrepareText(oldValue, QString()); - body->addLogEntryOriginal(id, tr::lng_admin_log_previous_description(tr::now), oldDescription); + const auto oldDescription = PrepareText(oldValue, QString()); + body->addLogEntryOriginal( + id, + tr::lng_admin_log_previous_description(tr::now), + oldDescription); } addPart(body); }; - auto createChangeUsername = [&](const MTPDchannelAdminLogEventActionChangeUsername &action) { - auto newValue = qs(action.vnew_value()); - auto oldValue = qs(action.vprev_value()); - auto text = (channel->isMegagroup() + const auto createChangeUsername = [&](const LogUsername &action) { + const auto newValue = qs(action.vnew_value()); + const auto oldValue = qs(action.vprev_value()); + const auto text = (channel->isMegagroup() ? (newValue.isEmpty() ? tr::lng_admin_log_removed_link_group : tr::lng_admin_log_changed_link_group) @@ -626,29 +755,27 @@ void GenerateItems( )(tr::now, lt_from, fromLinkText); addSimpleServiceMessage(text); - auto newLink = newValue.isEmpty() - ? TextWithEntities() - : PrepareText( - history->session().createInternalLinkFull(newValue), - QString()); - auto body = makeSimpleTextMessage(newValue.isEmpty() + const auto body = makeSimpleTextMessage(newValue.isEmpty() ? TextWithEntities() : PrepareText( history->session().createInternalLinkFull(newValue), QString())); if (!oldValue.isEmpty()) { - auto oldLink = PrepareText( + const auto oldLink = PrepareText( history->session().createInternalLinkFull(oldValue), QString()); - body->addLogEntryOriginal(id, tr::lng_admin_log_previous_link(tr::now), oldLink); + body->addLogEntryOriginal( + id, + tr::lng_admin_log_previous_link(tr::now), + oldLink); } addPart(body); }; - auto createChangePhoto = [&](const MTPDchannelAdminLogEventActionChangePhoto &action) { + const auto createChangePhoto = [&](const LogPhoto &action) { action.vnew_photo().match([&](const MTPDphoto &data) { - auto photo = history->owner().processPhoto(data); - auto text = (channel->isMegagroup() + const auto photo = history->owner().processPhoto(data); + const auto text = (channel->isMegagroup() ? tr::lng_admin_log_changed_photo_group : tr::lng_admin_log_changed_photo_channel)( tr::now, @@ -656,7 +783,7 @@ void GenerateItems( fromLinkText); addSimpleServiceMessage(text, photo); }, [&](const MTPDphotoEmpty &data) { - auto text = (channel->isMegagroup() + const auto text = (channel->isMegagroup() ? tr::lng_admin_log_removed_photo_group : tr::lng_admin_log_removed_photo_channel)( tr::now, @@ -666,31 +793,34 @@ void GenerateItems( }); }; - auto createToggleInvites = [&](const MTPDchannelAdminLogEventActionToggleInvites &action) { - auto enabled = (action.vnew_value().type() == mtpc_boolTrue); - auto text = (enabled + const auto createToggleInvites = [&](const LogInvites &action) { + const auto enabled = (action.vnew_value().type() == mtpc_boolTrue); + const auto text = (enabled ? tr::lng_admin_log_invites_enabled : tr::lng_admin_log_invites_disabled); addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; - auto createToggleSignatures = [&](const MTPDchannelAdminLogEventActionToggleSignatures &action) { - auto enabled = (action.vnew_value().type() == mtpc_boolTrue); - auto text = (enabled + const auto createToggleSignatures = [&](const LogSign &action) { + const auto enabled = (action.vnew_value().type() == mtpc_boolTrue); + const auto text = (enabled ? tr::lng_admin_log_signatures_enabled : tr::lng_admin_log_signatures_disabled); addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; - auto createUpdatePinned = [&](const MTPDchannelAdminLogEventActionUpdatePinned &action) { + const auto createUpdatePinned = [&](const LogPin &action) { action.vmessage().match([&](const MTPDmessage &data) { const auto pinned = data.is_pinned(); - auto text = (pinned + const auto text = (pinned ? tr::lng_admin_log_pinned_message - : tr::lng_admin_log_unpinned_message)(tr::now, lt_from, fromLinkText); + : tr::lng_admin_log_unpinned_message)( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); - auto detachExistingItem = false; + const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), @@ -699,15 +829,21 @@ void GenerateItems( detachExistingItem), ExtractSentDate(action.vmessage())); }, [&](const auto &) { - auto text = tr::lng_admin_log_unpinned_message(tr::now, lt_from, fromLinkText); + const auto text = tr::lng_admin_log_unpinned_message( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); }); }; - auto createEditMessage = [&](const MTPDchannelAdminLogEventActionEditMessage &action) { - auto newValue = ExtractEditedText(session, action.vnew_message()); - auto canHaveCaption = MediaCanHaveCaption(action.vnew_message()); - auto text = (!canHaveCaption + const auto createEditMessage = [&](const LogEdit &action) { + const auto newValue = ExtractEditedText( + session, + action.vnew_message()); + const auto canHaveCaption = MediaCanHaveCaption( + action.vnew_message()); + const auto text = (!canHaveCaption ? tr::lng_admin_log_edited_message : newValue.text.isEmpty() ? tr::lng_admin_log_removed_caption @@ -717,15 +853,19 @@ void GenerateItems( fromLinkText); addSimpleServiceMessage(text); - auto oldValue = ExtractEditedText(session, action.vprev_message()); - auto detachExistingItem = false; - auto body = history->createItem( + auto oldValue = ExtractEditedText( + session, + action.vprev_message()); + const auto detachExistingItem = false; + const auto body = history->createItem( history->nextNonHistoryEntryId(), PrepareLogMessage(action.vnew_message(), date), MessageFlag::AdminLogEntry, detachExistingItem); if (oldValue.text.isEmpty()) { - oldValue = PrepareText(QString(), tr::lng_admin_log_empty_text(tr::now)); + oldValue = PrepareText( + QString(), + tr::lng_admin_log_empty_text(tr::now)); } body->addLogEntryOriginal( @@ -737,11 +877,14 @@ void GenerateItems( addPart(body); }; - auto createDeleteMessage = [&](const MTPDchannelAdminLogEventActionDeleteMessage &action) { - auto text = tr::lng_admin_log_deleted_message(tr::now, lt_from, fromLinkText); + const auto createDeleteMessage = [&](const LogDelete &action) { + const auto text = tr::lng_admin_log_deleted_message( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); - auto detachExistingItem = false; + const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), @@ -751,58 +894,76 @@ void GenerateItems( ExtractSentDate(action.vmessage())); }; - auto createParticipantJoin = [&]() { - auto text = (channel->isMegagroup() + const auto createParticipantJoin = [&]() { + const auto text = (channel->isMegagroup() ? tr::lng_admin_log_participant_joined : tr::lng_admin_log_participant_joined_channel); addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; - auto createParticipantLeave = [&]() { - auto text = (channel->isMegagroup() + const auto createParticipantLeave = [&]() { + const auto text = (channel->isMegagroup() ? tr::lng_admin_log_participant_left : tr::lng_admin_log_participant_left_channel); addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; - auto createParticipantInvite = [&](const MTPDchannelAdminLogEventActionParticipantInvite &action) { + const auto createParticipantInvite = [&](const LogInvite &action) { addSimpleTextMessage( GenerateParticipantChangeText(channel, action.vparticipant())); }; - auto createParticipantToggleBan = [&](const MTPDchannelAdminLogEventActionParticipantToggleBan &action) { + const auto createParticipantToggleBan = [&](const LogBan &action) { addSimpleTextMessage( - GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant())); + GenerateParticipantChangeText( + channel, + action.vnew_participant(), + &action.vprev_participant())); }; - auto createParticipantToggleAdmin = [&](const MTPDchannelAdminLogEventActionParticipantToggleAdmin &action) { - if (action.vnew_participant().type() == mtpc_channelParticipantAdmin - && action.vprev_participant().type() == mtpc_channelParticipantCreator) { + const auto createParticipantToggleAdmin = [&](const LogPromote &action) { + if ((action.vnew_participant().type() == mtpc_channelParticipantAdmin) + && (action.vprev_participant().type() + == mtpc_channelParticipantCreator)) { // In case of ownership transfer we show that message in // the "User > Creator" part and skip the "Creator > Admin" part. return; } addSimpleTextMessage( - GenerateParticipantChangeText(channel, action.vnew_participant(), &action.vprev_participant())); + GenerateParticipantChangeText( + channel, + action.vnew_participant(), + &action.vprev_participant())); }; - auto createChangeStickerSet = [&](const MTPDchannelAdminLogEventActionChangeStickerSet &action) { - auto set = action.vnew_stickerset(); - auto removed = (set.type() == mtpc_inputStickerSetEmpty); + const auto createChangeStickerSet = [&](const LogSticker &action) { + const auto set = action.vnew_stickerset(); + const auto removed = (set.type() == mtpc_inputStickerSetEmpty); if (removed) { - auto text = tr::lng_admin_log_removed_stickers_group(tr::now, lt_from, fromLinkText); + const auto text = tr::lng_admin_log_removed_stickers_group( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); } else { - auto text = tr::lng_admin_log_changed_stickers_group( + const auto text = tr::lng_admin_log_changed_stickers_group( tr::now, lt_from, fromLinkText, lt_sticker_set, - textcmdLink(2, tr::lng_admin_log_changed_stickers_set(tr::now))); - auto setLink = std::make_shared<LambdaClickHandler>([set] { - Ui::show(Box<StickerSetBox>( - App::wnd()->sessionController(), - Data::FromInputSet(set))); + textcmdLink( + 2, + tr::lng_admin_log_changed_stickers_set(tr::now))); + const auto setLink = std::make_shared<LambdaClickHandler>([=]( + ClickContext context) { + const auto my = context.other.value<ClickHandlerContext>(); + if (const auto controller = my.sessionWindow.get()) { + controller->show( + Box<StickerSetBox>( + controller, + Data::FromInputSet(set)), + Ui::LayerOption::CloseOther); + } }); auto message = HistoryService::PreparedText { text }; message.links.push_back(fromLink); @@ -816,15 +977,17 @@ void GenerateItems( } }; - auto createTogglePreHistoryHidden = [&](const MTPDchannelAdminLogEventActionTogglePreHistoryHidden &action) { - auto hidden = (action.vnew_value().type() == mtpc_boolTrue); - auto text = (hidden + const auto createTogglePreHistoryHidden = [&]( + const LogPreHistory &action) { + const auto hidden = (action.vnew_value().type() == mtpc_boolTrue); + const auto text = (hidden ? tr::lng_admin_log_history_made_hidden : tr::lng_admin_log_history_made_visible); addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; - auto createDefaultBannedRights = [&](const MTPDchannelAdminLogEventActionDefaultBannedRights &action) { + const auto createDefaultBannedRights = [&]( + const LogPermissions &action) { addSimpleTextMessage( GenerateDefaultBannedRightsChangeText( channel, @@ -832,11 +995,14 @@ void GenerateItems( ChatRestrictionsInfo(action.vprev_banned_rights()))); }; - auto createStopPoll = [&](const MTPDchannelAdminLogEventActionStopPoll &action) { - auto text = tr::lng_admin_log_stopped_poll(tr::now, lt_from, fromLinkText); + const auto createStopPoll = [&](const LogPoll &action) { + const auto text = tr::lng_admin_log_stopped_poll( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); - auto detachExistingItem = false; + const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), @@ -846,10 +1012,11 @@ void GenerateItems( ExtractSentDate(action.vmessage())); }; - auto createChangeLinkedChat = [&](const MTPDchannelAdminLogEventActionChangeLinkedChat &action) { - const auto now = history->owner().channelLoaded(action.vnew_value().v); + const auto createChangeLinkedChat = [&](const LogDiscussion &action) { + const auto now = history->owner().channelLoaded( + action.vnew_value().v); if (!now) { - auto text = (broadcast + const auto text = (broadcast ? tr::lng_admin_log_removed_linked_chat : tr::lng_admin_log_removed_linked_channel)( tr::now, @@ -857,7 +1024,7 @@ void GenerateItems( fromLinkText); addSimpleServiceMessage(text); } else { - auto text = (broadcast + const auto text = (broadcast ? tr::lng_admin_log_changed_linked_chat : tr::lng_admin_log_changed_linked_channel)( tr::now, @@ -865,7 +1032,7 @@ void GenerateItems( fromLinkText, lt_chat, textcmdLink(2, now->name)); - auto chatLink = std::make_shared<LambdaClickHandler>([=] { + const auto chatLink = std::make_shared<LambdaClickHandler>([=] { Ui::showPeerHistory(now, ShowAtUnreadMsgId); }); auto message = HistoryService::PreparedText{ text }; @@ -880,10 +1047,11 @@ void GenerateItems( } }; - auto createChangeLocation = [&](const MTPDchannelAdminLogEventActionChangeLocation &action) { + const auto createChangeLocation = [&](const LogLocation &action) { action.vnew_value().match([&](const MTPDchannelLocation &data) { const auto address = qs(data.vaddress()); - const auto link = data.vgeo_point().match([&](const MTPDgeoPoint &data) { + const auto link = data.vgeo_point().match([&]( + const MTPDgeoPoint &data) { return textcmdLink( LocationClickHandler::Url(Data::LocationPoint(data)), address); @@ -898,16 +1066,25 @@ void GenerateItems( link); addSimpleServiceMessage(text); }, [&](const MTPDchannelLocationEmpty &) { - const auto text = tr::lng_admin_log_removed_location_chat(tr::now, lt_from, fromLinkText); + const auto text = tr::lng_admin_log_removed_location_chat( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); }); }; - auto createToggleSlowMode = [&](const MTPDchannelAdminLogEventActionToggleSlowMode &action) { + const auto createToggleSlowMode = [&](const LogSlowMode &action) { if (const auto seconds = action.vnew_value().v) { const auto duration = (seconds >= 60) - ? tr::lng_admin_log_slow_mode_minutes(tr::now, lt_count, seconds / 60) - : tr::lng_admin_log_slow_mode_seconds(tr::now, lt_count, seconds); + ? tr::lng_admin_log_slow_mode_minutes( + tr::now, + lt_count, + seconds / 60) + : tr::lng_admin_log_slow_mode_seconds( + tr::now, + lt_count, + seconds); const auto text = tr::lng_admin_log_changed_slow_mode( tr::now, lt_from, @@ -916,32 +1093,44 @@ void GenerateItems( duration); addSimpleServiceMessage(text); } else { - const auto text = tr::lng_admin_log_removed_slow_mode(tr::now, lt_from, fromLinkText); + const auto text = tr::lng_admin_log_removed_slow_mode( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); } }; - auto createStartGroupCall = [&](const MTPDchannelAdminLogEventActionStartGroupCall &data) { + const auto createStartGroupCall = [&](const LogStartCall &data) { const auto text = (broadcast ? tr::lng_admin_log_started_group_call_channel - : tr::lng_admin_log_started_group_call)(tr::now, lt_from, fromLinkText); + : tr::lng_admin_log_started_group_call)( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); }; - auto createDiscardGroupCall = [&](const MTPDchannelAdminLogEventActionDiscardGroupCall &data) { + const auto createDiscardGroupCall = [&](const LogDiscardCall &data) { const auto text = (broadcast ? tr::lng_admin_log_discarded_group_call_channel - : tr::lng_admin_log_discarded_group_call)(tr::now, lt_from, fromLinkText); + : tr::lng_admin_log_discarded_group_call)( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); }; - auto groupCallParticipantPeer = [&](const MTPGroupCallParticipant &data) { + const auto groupCallParticipantPeer = [&]( + const MTPGroupCallParticipant &data) { return data.match([&](const MTPDgroupCallParticipant &data) { return history->owner().peer(peerFromMTP(data.vpeer())); }); }; - auto addServiceMessageWithLink = [&](const QString &text, const ClickHandlerPtr &link) { + const auto addServiceMessageWithLink = [&]( + const QString &text, + const ClickHandlerPtr &link) { auto message = HistoryService::PreparedText{ text }; message.links.push_back(fromLink); message.links.push_back(link); @@ -953,11 +1142,14 @@ void GenerateItems( peerToUser(from->id))); }; - auto createParticipantMute = [&](const MTPDchannelAdminLogEventActionParticipantMute &data) { - const auto participantPeer = groupCallParticipantPeer(data.vparticipant()); + const auto createParticipantMute = [&](const LogMute &data) { + const auto participantPeer = groupCallParticipantPeer( + data.vparticipant()); const auto participantPeerLink = participantPeer->createOpenLink(); - const auto participantPeerLinkText = textcmdLink(2, participantPeer->name); - auto text = (broadcast + const auto participantPeerLinkText = textcmdLink( + 2, + participantPeer->name); + const auto text = (broadcast ? tr::lng_admin_log_muted_participant_channel : tr::lng_admin_log_muted_participant)( tr::now, @@ -968,11 +1160,14 @@ void GenerateItems( addServiceMessageWithLink(text, participantPeerLink); }; - auto createParticipantUnmute = [&](const MTPDchannelAdminLogEventActionParticipantUnmute &data) { - const auto participantPeer = groupCallParticipantPeer(data.vparticipant()); + const auto createParticipantUnmute = [&](const LogUnmute &data) { + const auto participantPeer = groupCallParticipantPeer( + data.vparticipant()); const auto participantPeerLink = participantPeer->createOpenLink(); - const auto participantPeerLinkText = textcmdLink(2, participantPeer->name); - auto text = (broadcast + const auto participantPeerLinkText = textcmdLink( + 2, + participantPeer->name); + const auto text = (broadcast ? tr::lng_admin_log_unmuted_participant_channel : tr::lng_admin_log_unmuted_participant)( tr::now, @@ -983,22 +1178,30 @@ void GenerateItems( addServiceMessageWithLink(text, participantPeerLink); }; - auto createToggleGroupCallSetting = [&](const MTPDchannelAdminLogEventActionToggleGroupCallSetting &data) { + const auto createToggleGroupCallSetting = [&]( + const LogCallSetting &data) { const auto text = (mtpIsTrue(data.vjoin_muted()) ? (broadcast ? tr::lng_admin_log_disallowed_unmute_self_channel : tr::lng_admin_log_disallowed_unmute_self) : (broadcast ? tr::lng_admin_log_allowed_unmute_self_channel - : tr::lng_admin_log_allowed_unmute_self))(tr::now, lt_from, fromLinkText); + : tr::lng_admin_log_allowed_unmute_self))( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); }; - auto addInviteLinkServiceMessage = [&](const QString &text, const MTPExportedChatInvite &data, ClickHandlerPtr additional = nullptr) { + const auto addInviteLinkServiceMessage = [&]( + const QString &text, + const MTPExportedChatInvite &data, + ClickHandlerPtr additional = nullptr) { auto message = HistoryService::PreparedText{ text }; message.links.push_back(fromLink); if (!ExtractInviteLink(data).endsWith("...")) { - message.links.push_back(std::make_shared<UrlClickHandler>(InternalInviteLinkUrl(data))); + message.links.push_back(std::make_shared<UrlClickHandler>( + InternalInviteLinkUrl(data))); } if (additional) { message.links.push_back(std::move(additional)); @@ -1012,8 +1215,9 @@ void GenerateItems( nullptr)); }; - auto createParticipantJoinByInvite = [&](const MTPDchannelAdminLogEventActionParticipantJoinByInvite &data) { - auto text = (channel->isMegagroup() + const auto createParticipantJoinByInvite = [&]( + const LogJoinByInvite &data) { + const auto text = (channel->isMegagroup() ? tr::lng_admin_log_participant_joined_by_link : tr::lng_admin_log_participant_joined_by_link_channel); addInviteLinkServiceMessage( @@ -1026,7 +1230,7 @@ void GenerateItems( data.vinvite()); }; - auto createExportedInviteDelete = [&](const MTPDchannelAdminLogEventActionExportedInviteDelete &data) { + const auto createExportedInviteDelete = [&](const LogInviteDelete &data) { addInviteLinkServiceMessage( tr::lng_admin_log_delete_invite_link( tr::now, @@ -1037,7 +1241,7 @@ void GenerateItems( data.vinvite()); }; - auto createExportedInviteRevoke = [&](const MTPDchannelAdminLogEventActionExportedInviteRevoke &data) { + const auto createExportedInviteRevoke = [&](const LogInviteRevoke &data) { addInviteLinkServiceMessage( tr::lng_admin_log_revoke_invite_link( tr::now, @@ -1048,15 +1252,20 @@ void GenerateItems( data.vinvite()); }; - auto createExportedInviteEdit = [&](const MTPDchannelAdminLogEventActionExportedInviteEdit &data) { + const auto createExportedInviteEdit = [&](const LogInviteEdit &data) { addSimpleTextMessage( - GenerateInviteLinkChangeText(data.vnew_invite(), data.vprev_invite())); + GenerateInviteLinkChangeText( + data.vnew_invite(), + data.vprev_invite())); }; - auto createParticipantVolume = [&](const MTPDchannelAdminLogEventActionParticipantVolume &data) { - const auto participantPeer = groupCallParticipantPeer(data.vparticipant()); + const auto createParticipantVolume = [&](const LogVolume &data) { + const auto participantPeer = groupCallParticipantPeer( + data.vparticipant()); const auto participantPeerLink = participantPeer->createOpenLink(); - const auto participantPeerLinkText = textcmdLink(2, participantPeer->name); + const auto participantPeerLinkText = textcmdLink( + 2, + participantPeer->name); const auto volume = data.vparticipant().match([&]( const MTPDgroupCallParticipant &data) { return data.vvolume().value_or(10000); @@ -1075,7 +1284,7 @@ void GenerateItems( addServiceMessageWithLink(text, participantPeerLink); }; - auto createChangeHistoryTTL = [&](const MTPDchannelAdminLogEventActionChangeHistoryTTL &data) { + const auto createChangeHistoryTTL = [&](const LogTTL &data) { const auto was = data.vprev_value().v; const auto now = data.vnew_value().v; const auto wrap = [](int duration) { @@ -1087,17 +1296,35 @@ void GenerateItems( ? tr::lng_manage_messages_ttl_after2(tr::now) : tr::lng_manage_messages_ttl_after3(tr::now); }; - auto text = !was - ? tr::lng_admin_log_messages_ttl_set(tr::now, lt_from, fromLinkText, lt_duration, wrap(now)) + const auto text = !was + ? tr::lng_admin_log_messages_ttl_set( + tr::now, + lt_from, + fromLinkText, + lt_duration, + wrap(now)) : !now - ? tr::lng_admin_log_messages_ttl_removed(tr::now, lt_from, fromLinkText, lt_duration, wrap(was)) - : tr::lng_admin_log_messages_ttl_changed(tr::now, lt_from, fromLinkText, lt_previous, wrap(was), lt_duration, wrap(now)); + ? tr::lng_admin_log_messages_ttl_removed( + tr::now, + lt_from, + fromLinkText, + lt_duration, + wrap(was)) + : tr::lng_admin_log_messages_ttl_changed( + tr::now, + lt_from, + fromLinkText, + lt_previous, + wrap(was), + lt_duration, + wrap(now)); addSimpleServiceMessage(text); }; - auto createParticipantJoinByRequest = [&](const MTPDchannelAdminLogEventActionParticipantJoinByRequest &data) { + const auto createParticipantJoinByRequest = [&]( + const LogJoinByRequest &data) { const auto user = channel->owner().user(UserId(data.vapproved_by())); - auto text = (channel->isMegagroup() + const auto text = (channel->isMegagroup() ? tr::lng_admin_log_participant_approved_by_link : tr::lng_admin_log_participant_approved_by_link_channel); const auto linkText = GenerateInviteLinkLink(data.vinvite()); @@ -1115,7 +1342,7 @@ void GenerateItems( user->createOpenLink()); }; - auto createToggleNoForwards = [&](const MTPDchannelAdminLogEventActionToggleNoForwards &data) { + const auto createToggleNoForwards = [&](const LogNoForwards &data) { const auto disabled = (data.vnew_value().type() == mtpc_boolTrue); const auto text = (disabled ? tr::lng_admin_log_forwards_disabled @@ -1123,11 +1350,14 @@ void GenerateItems( addSimpleServiceMessage(text(tr::now, lt_from, fromLinkText)); }; - auto createSendMessage = [&](const MTPDchannelAdminLogEventActionSendMessage &data) { - auto text = tr::lng_admin_log_sent_message(tr::now, lt_from, fromLinkText); + const auto createSendMessage = [&](const LogActionSendMessage &data) { + const auto text = tr::lng_admin_log_sent_message( + tr::now, + lt_from, + fromLinkText); addSimpleServiceMessage(text); - auto detachExistingItem = false; + const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), @@ -1137,75 +1367,75 @@ void GenerateItems( ExtractSentDate(data.vmessage())); }; - action.match([&](const MTPDchannelAdminLogEventActionChangeTitle &data) { + action.match([&](const LogTitle &data) { createChangeTitle(data); - }, [&](const MTPDchannelAdminLogEventActionChangeAbout &data) { + }, [&](const LogAbout &data) { createChangeAbout(data); - }, [&](const MTPDchannelAdminLogEventActionChangeUsername &data) { + }, [&](const LogUsername &data) { createChangeUsername(data); - }, [&](const MTPDchannelAdminLogEventActionChangePhoto &data) { + }, [&](const LogPhoto &data) { createChangePhoto(data); - }, [&](const MTPDchannelAdminLogEventActionToggleInvites &data) { + }, [&](const LogInvites &data) { createToggleInvites(data); - }, [&](const MTPDchannelAdminLogEventActionToggleSignatures &data) { + }, [&](const LogSign &data) { createToggleSignatures(data); - }, [&](const MTPDchannelAdminLogEventActionUpdatePinned &data) { + }, [&](const LogPin &data) { createUpdatePinned(data); - }, [&](const MTPDchannelAdminLogEventActionEditMessage &data) { + }, [&](const LogEdit &data) { createEditMessage(data); - }, [&](const MTPDchannelAdminLogEventActionDeleteMessage &data) { + }, [&](const LogDelete &data) { createDeleteMessage(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantJoin &) { + }, [&](const LogJoin &) { createParticipantJoin(); - }, [&](const MTPDchannelAdminLogEventActionParticipantLeave &) { + }, [&](const LogLeave &) { createParticipantLeave(); - }, [&](const MTPDchannelAdminLogEventActionParticipantInvite &data) { + }, [&](const LogInvite &data) { createParticipantInvite(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantToggleBan &data) { + }, [&](const LogBan &data) { createParticipantToggleBan(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantToggleAdmin &data) { + }, [&](const LogPromote &data) { createParticipantToggleAdmin(data); - }, [&](const MTPDchannelAdminLogEventActionChangeStickerSet &data) { + }, [&](const LogSticker &data) { createChangeStickerSet(data); - }, [&](const MTPDchannelAdminLogEventActionTogglePreHistoryHidden &data) { + }, [&](const LogPreHistory &data) { createTogglePreHistoryHidden(data); - }, [&](const MTPDchannelAdminLogEventActionDefaultBannedRights &data) { + }, [&](const LogPermissions &data) { createDefaultBannedRights(data); - }, [&](const MTPDchannelAdminLogEventActionStopPoll &data) { + }, [&](const LogPoll &data) { createStopPoll(data); - }, [&](const MTPDchannelAdminLogEventActionChangeLinkedChat &data) { + }, [&](const LogDiscussion &data) { createChangeLinkedChat(data); - }, [&](const MTPDchannelAdminLogEventActionChangeLocation &data) { + }, [&](const LogLocation &data) { createChangeLocation(data); - }, [&](const MTPDchannelAdminLogEventActionToggleSlowMode &data) { + }, [&](const LogSlowMode &data) { createToggleSlowMode(data); - }, [&](const MTPDchannelAdminLogEventActionStartGroupCall &data) { + }, [&](const LogStartCall &data) { createStartGroupCall(data); - }, [&](const MTPDchannelAdminLogEventActionDiscardGroupCall &data) { + }, [&](const LogDiscardCall &data) { createDiscardGroupCall(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantMute &data) { + }, [&](const LogMute &data) { createParticipantMute(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantUnmute &data) { + }, [&](const LogUnmute &data) { createParticipantUnmute(data); - }, [&](const MTPDchannelAdminLogEventActionToggleGroupCallSetting &data) { + }, [&](const LogCallSetting &data) { createToggleGroupCallSetting(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantJoinByInvite &data) { + }, [&](const LogJoinByInvite &data) { createParticipantJoinByInvite(data); - }, [&](const MTPDchannelAdminLogEventActionExportedInviteDelete &data) { + }, [&](const LogInviteDelete &data) { createExportedInviteDelete(data); - }, [&](const MTPDchannelAdminLogEventActionExportedInviteRevoke &data) { + }, [&](const LogInviteRevoke &data) { createExportedInviteRevoke(data); - }, [&](const MTPDchannelAdminLogEventActionExportedInviteEdit &data) { + }, [&](const LogInviteEdit &data) { createExportedInviteEdit(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantVolume &data) { + }, [&](const LogVolume &data) { createParticipantVolume(data); - }, [&](const MTPDchannelAdminLogEventActionChangeHistoryTTL &data) { + }, [&](const LogTTL &data) { createChangeHistoryTTL(data); - }, [&](const MTPDchannelAdminLogEventActionParticipantJoinByRequest &data) { + }, [&](const LogJoinByRequest &data) { createParticipantJoinByRequest(data); - }, [&](const MTPDchannelAdminLogEventActionToggleNoForwards &data) { + }, [&](const LogNoForwards &data) { createToggleNoForwards(data); - }, [&](const MTPDchannelAdminLogEventActionSendMessage &data) { + }, [&](const LogActionSendMessage &data) { createSendMessage(data); }); } From 30cd3cb68144521eee9026cd077137e6e8cbd4e0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 20 Nov 2021 09:46:28 +0300 Subject: [PATCH 086/180] Removed MTP* from AdminLog::FilterValue. --- .../admin_log/history_admin_log_filter.cpp | 47 +++++++++++++------ .../admin_log/history_admin_log_inner.cpp | 26 +++++++++- .../admin_log/history_admin_log_section.h | 25 +++++++++- 3 files changed, 81 insertions(+), 17 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp index 69c81cd14..6030ca61a 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp @@ -183,7 +183,7 @@ private: QPointer<Ui::Checkbox> _allFlags; base::flat_map< - MTPDchannelAdminLogEventsFilter::Flags, + FilterValue::Flags, QPointer<Ui::Checkbox>> _filterFlags; QPointer<Ui::Checkbox> _allUsers; @@ -238,8 +238,8 @@ void FilterBox::Inner::createAllActionsCheckbox(const FilterValue &filter) { } void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) { - using Flag = MTPDchannelAdminLogEventsFilter::Flag; - using Flags = MTPDchannelAdminLogEventsFilter::Flags; + using Flag = FilterValue::Flag; + using Flags = FilterValue::Flags; auto addFlag = [this, &filter](Flags flag, QString &&text) { auto checked = (filter.flags == 0) || (filter.flags & flag); auto checkbox = addRow(object_ptr<Ui::Checkbox>(this, std::move(text), checked, st::defaultBoxCheckbox), st::adminLogFilterLittleSkip); @@ -264,21 +264,40 @@ void FilterBox::Inner::createActionsCheckboxes(const FilterValue &filter) { }; auto isGroup = _channel->isMegagroup(); if (isGroup) { - addFlag(Flag::f_ban | Flag::f_unban | Flag::f_kick | Flag::f_unkick, tr::lng_admin_log_filter_restrictions(tr::now)); + addFlag( + Flag::Ban + | Flag::Unban + | Flag::Kick + | Flag::Unkick, + tr::lng_admin_log_filter_restrictions(tr::now)); } - addFlag(Flag::f_promote | Flag::f_demote, tr::lng_admin_log_filter_admins_new(tr::now)); - addFlag(Flag::f_join | Flag::f_invite, tr::lng_admin_log_filter_members_new(tr::now)); - addFlag(Flag::f_info | Flag::f_settings, _channel->isMegagroup() ? tr::lng_admin_log_filter_info_group(tr::now) : tr::lng_admin_log_filter_info_channel(tr::now)); - addFlag(Flag::f_delete, tr::lng_admin_log_filter_messages_deleted(tr::now)); - addFlag(Flag::f_edit, tr::lng_admin_log_filter_messages_edited(tr::now)); + addFlag( + Flag::Promote | Flag::Demote, + tr::lng_admin_log_filter_admins_new(tr::now)); + addFlag( + Flag::Join | Flag::Invite, + tr::lng_admin_log_filter_members_new(tr::now)); + addFlag( + Flag::Info | Flag::Settings, + _channel->isMegagroup() + ? tr::lng_admin_log_filter_info_group(tr::now) + : tr::lng_admin_log_filter_info_channel(tr::now)); + addFlag(Flag::Delete, tr::lng_admin_log_filter_messages_deleted(tr::now)); + addFlag(Flag::Edit, tr::lng_admin_log_filter_messages_edited(tr::now)); if (isGroup) { - addFlag(Flag::f_pinned, tr::lng_admin_log_filter_messages_pinned(tr::now)); - addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats(tr::now)); + addFlag( + Flag::Pinned, + tr::lng_admin_log_filter_messages_pinned(tr::now)); + addFlag( + Flag::GroupCall, + tr::lng_admin_log_filter_voice_chats(tr::now)); } else { - addFlag(Flag::f_group_call, tr::lng_admin_log_filter_voice_chats_channel(tr::now)); + addFlag( + Flag::GroupCall, + tr::lng_admin_log_filter_voice_chats_channel(tr::now)); } - addFlag(Flag::f_invites, tr::lng_admin_log_filter_invite_links(tr::now)); - addFlag(Flag::f_leave, tr::lng_admin_log_filter_members_removed(tr::now)); + addFlag(Flag::Invites, tr::lng_admin_log_filter_invite_links(tr::now)); + addFlag(Flag::Leave, tr::lng_admin_log_filter_members_removed(tr::now)); } void FilterBox::Inner::createAllUsersCheckbox(const FilterValue &filter) { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 405f015ba..c8ea855df 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -723,7 +723,29 @@ void InnerWidget::preloadMore(Direction direction) { } auto flags = MTPchannels_GetAdminLog::Flags(0); - auto filter = MTP_channelAdminLogEventsFilter(MTP_flags(_filter.flags)); + const auto filter = [&] { + using Flag = MTPDchannelAdminLogEventsFilter::Flag; + using LocalFlag = FilterValue::Flag; + const auto empty = MTPDchannelAdminLogEventsFilter::Flags(0); + const auto f = _filter.flags; + return empty + | ((f & LocalFlag::Join) ? Flag::f_join : empty) + | ((f & LocalFlag::Leave) ? Flag::f_leave : empty) + | ((f & LocalFlag::Invite) ? Flag::f_invite : empty) + | ((f & LocalFlag::Ban) ? Flag::f_ban : empty) + | ((f & LocalFlag::Unban) ? Flag::f_unban : empty) + | ((f & LocalFlag::Kick) ? Flag::f_kick : empty) + | ((f & LocalFlag::Unkick) ? Flag::f_unkick : empty) + | ((f & LocalFlag::Promote) ? Flag::f_promote : empty) + | ((f & LocalFlag::Demote) ? Flag::f_demote : empty) + | ((f & LocalFlag::Info) ? Flag::f_info : empty) + | ((f & LocalFlag::Settings) ? Flag::f_settings : empty) + | ((f & LocalFlag::Pinned) ? Flag::f_pinned : empty) + | ((f & LocalFlag::Edit) ? Flag::f_edit : empty) + | ((f & LocalFlag::Delete) ? Flag::f_delete : empty) + | ((f & LocalFlag::GroupCall) ? Flag::f_group_call : empty) + | ((f & LocalFlag::Invites) ? Flag::f_invites : empty); + }(); if (_filter.flags != 0) { flags |= MTPchannels_GetAdminLog::Flag::f_events_filter; } @@ -744,7 +766,7 @@ void InnerWidget::preloadMore(Direction direction) { MTP_flags(flags), _channel->inputChannel, MTP_string(_searchQuery), - filter, + MTP_channelAdminLogEventsFilter(MTP_flags(filter)), MTP_vector<MTPInputUser>(admins), MTP_long(maxId), MTP_long(minId), diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h index 791ffd150..fea67d9a2 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h @@ -29,8 +29,31 @@ class InnerWidget; class SectionMemento; struct FilterValue { + enum class Flag : uint32 { + Join = (1U << 0), + Leave = (1U << 1), + Invite = (1U << 2), + Ban = (1U << 3), + Unban = (1U << 4), + Kick = (1U << 5), + Unkick = (1U << 6), + Promote = (1U << 7), + Demote = (1U << 8), + Info = (1U << 9), + Settings = (1U << 10), + Pinned = (1U << 11), + Edit = (1U << 12), + Delete = (1U << 13), + GroupCall = (1U << 14), + Invites = (1U << 15), + + MAX_FIELD = (1U << 15), + }; + using Flags = base::flags<Flag>; + friend inline constexpr bool is_flag_type(Flag) { return true; }; + // Empty "flags" means all events. - MTPDchannelAdminLogEventsFilter::Flags flags = 0; + Flags flags = 0; std::vector<not_null<UserData*>> admins; bool allUsers = true; }; From 0eee937e6df4f24f83965829cf23333b40bae905 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 21 Nov 2021 22:48:08 +0300 Subject: [PATCH 087/180] Moved ChatRestrictionsInfo and ChatAdminRightsInfo to separated file. --- Telegram/CMakeLists.txt | 2 + .../boxes/peers/edit_participant_box.h | 2 +- .../boxes/peers/edit_peer_permissions_box.h | 2 +- Telegram/SourceFiles/data/data_channel.cpp | 4 +- Telegram/SourceFiles/data/data_channel.h | 1 + Telegram/SourceFiles/data/data_chat.cpp | 24 +++---- Telegram/SourceFiles/data/data_chat.h | 14 ++-- .../data/data_chat_participant_status.cpp | 64 +++++++++++++++++ .../data/data_chat_participant_status.h | 68 ++++++++++++++++++ Telegram/SourceFiles/data/data_peer.cpp | 36 ---------- Telegram/SourceFiles/data/data_peer.h | 72 +------------------ .../view/history_view_scheduled_section.cpp | 1 + .../inline_bots/inline_results_inner.cpp | 1 + .../mac/touchbar/items/mac_scrubber_item.mm | 1 + 14 files changed, 160 insertions(+), 132 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_chat_participant_status.cpp create mode 100644 Telegram/SourceFiles/data/data_chat_participant_status.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index bfd931857..df6bfd1b5 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -400,6 +400,8 @@ PRIVATE data/data_chat.h data/data_chat_filters.cpp data/data_chat_filters.h + data/data_chat_participant_status.cpp + data/data_chat_participant_status.h data/data_changes.cpp data/data_changes.h data/data_channel.cpp diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index bbcf517a2..e9327ca85 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" #include "base/unique_qptr.h" -#include "data/data_peer.h" +#include "data/data_chat_participant_status.h" namespace MTP { class Error; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h index d11e18626..f4e23de39 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h @@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "boxes/abstract_box.h" -#include "data/data_peer.h" +#include "data/data_chat_participant_status.h" namespace Ui { class RoundButton; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index a6b6a95ca..1d9b4fce9 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -774,8 +774,8 @@ void ApplyMigration( void ApplyChannelUpdate( not_null<ChannelData*> channel, const MTPDupdateChatDefaultBannedRights &update) { - channel->setDefaultRestrictions(Data::ChatBannedRightsFlags( - update.vdefault_banned_rights())); + channel->setDefaultRestrictions(ChatRestrictionsInfo( + update.vdefault_banned_rights()).flags); } void ApplyChannelUpdate( diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 94bf7e645..ec5a0ff57 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_pts_waiter.h" #include "data/data_location.h" +#include "data/data_chat_participant_status.h" struct ChannelLocation { QString address; diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 59f1525d3..731b595bf 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -47,7 +47,7 @@ void ChatData::setPhoto(const MTPChatPhoto &photo) { ChatAdminRightsInfo ChatData::defaultAdminRights(not_null<UserData*> user) { const auto isCreator = (creator == peerToUser(user->id)) || (user->isSelf() && amCreator()); - using Flag = AdminRight; + using Flag = ChatAdminRight; return ChatAdminRightsInfo(Flag::Other | Flag::ChangeInfo | Flag::DeleteMessages @@ -60,7 +60,7 @@ ChatAdminRightsInfo ChatData::defaultAdminRights(not_null<UserData*> user) { bool ChatData::canWrite() const { // Duplicated in Data::CanWriteValue(). - return amIn() && !amRestricted(Restriction::SendMessages); + return amIn() && !amRestricted(ChatRestriction::SendMessages); } bool ChatData::allowsForwarding() const { @@ -68,12 +68,12 @@ bool ChatData::allowsForwarding() const { } bool ChatData::canEditInformation() const { - return amIn() && !amRestricted(Restriction::ChangeInfo); + return amIn() && !amRestricted(ChatRestriction::ChangeInfo); } bool ChatData::canEditPermissions() const { return amIn() - && (amCreator() || (adminRights() & AdminRight::BanUsers)); + && (amCreator() || (adminRights() & ChatAdminRight::BanUsers)); } bool ChatData::canEditUsername() const { @@ -87,15 +87,15 @@ bool ChatData::canEditPreHistoryHidden() const { bool ChatData::canDeleteMessages() const { return amCreator() - || (adminRights() & AdminRight::DeleteMessages); + || (adminRights() & ChatAdminRight::DeleteMessages); } bool ChatData::canAddMembers() const { - return amIn() && !amRestricted(Restriction::InviteUsers); + return amIn() && !amRestricted(ChatRestriction::InviteUsers); } bool ChatData::canSendPolls() const { - return amIn() && !amRestricted(Restriction::SendPolls); + return amIn() && !amRestricted(ChatRestriction::SendPolls); } bool ChatData::canAddAdmins() const { @@ -104,11 +104,11 @@ bool ChatData::canAddAdmins() const { bool ChatData::canBanMembers() const { return amCreator() - || (adminRights() & AdminRight::BanUsers); + || (adminRights() & ChatAdminRight::BanUsers); } bool ChatData::anyoneCanAddMembers() const { - return !(defaultRestrictions() & Restriction::InviteUsers); + return !(defaultRestrictions() & ChatRestriction::InviteUsers); } void ChatData::setName(const QString &newName) { @@ -142,7 +142,7 @@ void ChatData::setInviteLink(const QString &newInviteLink) { bool ChatData::canHaveInviteLink() const { return amCreator() - || (adminRights() & AdminRight::InviteUsers); + || (adminRights() & ChatAdminRight::InviteUsers); } void ChatData::setAdminRights(ChatAdminRights rights) { @@ -414,8 +414,8 @@ void ApplyChatUpdate( != ChatData::UpdateStatus::Good) { return; } - chat->setDefaultRestrictions(Data::ChatBannedRightsFlags( - update.vdefault_banned_rights())); + chat->setDefaultRestrictions(ChatRestrictionsInfo( + update.vdefault_banned_rights()).flags); } void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) { diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index 3f8f4ab05..69cd51627 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -8,6 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "data/data_peer.h" +#include "data/data_chat_participant_status.h" + +enum class ChatAdminRight; enum class ChatDataFlag { Left = (1 << 0), @@ -28,13 +31,6 @@ public: using Flag = ChatDataFlag; using Flags = Data::Flags<ChatDataFlags>; - using AdminRight = ChatAdminRight; - using Restriction = ChatRestriction; - using AdminRights = ChatAdminRights; - using Restrictions = ChatRestrictions; - using AdminRightFlags = Data::Flags<AdminRights>; - using RestrictionFlags = Data::Flags<Restrictions>; - ChatData(not_null<Data::Session*> owner, PeerId id); void setName(const QString &newName); @@ -197,8 +193,8 @@ private: Flags _flags; QString _inviteLink; - RestrictionFlags _defaultRestrictions; - AdminRightFlags _adminRights; + Data::Flags<ChatRestrictions> _defaultRestrictions; + Data::Flags<ChatAdminRights> _adminRights; int _version = 0; int _pendingRequestsCount = 0; std::vector<UserId> _recentRequesters; diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.cpp b/Telegram/SourceFiles/data/data_chat_participant_status.cpp new file mode 100644 index 000000000..e878775b5 --- /dev/null +++ b/Telegram/SourceFiles/data/data_chat_participant_status.cpp @@ -0,0 +1,64 @@ +/* +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 "data/data_chat_participant_status.h" + +namespace { + +[[nodiscard]] ChatAdminRights ChatAdminRightsFlags( + const MTPChatAdminRights &rights) { + return rights.match([](const MTPDchatAdminRights &data) { + return ChatAdminRights::from_raw(int32(data.vflags().v)); + }); +} + +[[nodiscard]] ChatRestrictions ChatBannedRightsFlags( + const MTPChatBannedRights &rights) { + return rights.match([](const MTPDchatBannedRights &data) { + return ChatRestrictions::from_raw(int32(data.vflags().v)); + }); +} + +[[nodiscard]] TimeId ChatBannedRightsUntilDate( + const MTPChatBannedRights &rights) { + return rights.match([](const MTPDchatBannedRights &data) { + return data.vuntil_date().v; + }); +} + +} // namespace + +ChatAdminRightsInfo::ChatAdminRightsInfo(const MTPChatAdminRights &rights) +: flags(ChatAdminRightsFlags(rights)) { +} + +ChatRestrictionsInfo::ChatRestrictionsInfo(const MTPChatBannedRights &rights) +: flags(ChatBannedRightsFlags(rights)) +, until(ChatBannedRightsUntilDate(rights)) { +} + +namespace Data { + +std::vector<ChatRestrictions> ListOfRestrictions() { + using Flag = ChatRestriction; + + return { + Flag::SendMessages, + Flag::SendMedia, + Flag::SendStickers + | Flag::SendGifs + | Flag::SendGames + | Flag::SendInline, + Flag::EmbedLinks, + Flag::SendPolls, + Flag::InviteUsers, + Flag::PinMessages, + Flag::ChangeInfo, + }; +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.h b/Telegram/SourceFiles/data/data_chat_participant_status.h new file mode 100644 index 000000000..7dff3e685 --- /dev/null +++ b/Telegram/SourceFiles/data/data_chat_participant_status.h @@ -0,0 +1,68 @@ +/* +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 + +enum class ChatAdminRight { + ChangeInfo = (1 << 0), + PostMessages = (1 << 1), + EditMessages = (1 << 2), + DeleteMessages = (1 << 3), + BanUsers = (1 << 4), + InviteUsers = (1 << 5), + PinMessages = (1 << 7), + AddAdmins = (1 << 9), + Anonymous = (1 << 10), + ManageCall = (1 << 11), + Other = (1 << 12), +}; +inline constexpr bool is_flag_type(ChatAdminRight) { return true; } +using ChatAdminRights = base::flags<ChatAdminRight>; + +enum class ChatRestriction { + ViewMessages = (1 << 0), + SendMessages = (1 << 1), + SendMedia = (1 << 2), + SendStickers = (1 << 3), + SendGifs = (1 << 4), + SendGames = (1 << 5), + SendInline = (1 << 6), + EmbedLinks = (1 << 7), + SendPolls = (1 << 8), + ChangeInfo = (1 << 10), + InviteUsers = (1 << 15), + PinMessages = (1 << 17), +}; +inline constexpr bool is_flag_type(ChatRestriction) { return true; } +using ChatRestrictions = base::flags<ChatRestriction>; + +struct ChatAdminRightsInfo { + ChatAdminRightsInfo() = default; + explicit ChatAdminRightsInfo(ChatAdminRights flags) : flags(flags) { + } + explicit ChatAdminRightsInfo(const MTPChatAdminRights &rights); + + ChatAdminRights flags; +}; + +struct ChatRestrictionsInfo { + ChatRestrictionsInfo() = default; + ChatRestrictionsInfo(ChatRestrictions flags, TimeId until) + : flags(flags) + , until(until) { + } + explicit ChatRestrictionsInfo(const MTPChatBannedRights &rights); + + ChatRestrictions flags; + TimeId until = 0; +}; + +namespace Data { + +std::vector<ChatRestrictions> ListOfRestrictions(); + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 0239f23a4..112b42636 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -1069,24 +1069,6 @@ void PeerData::setMessagesTTL(TimeId period) { namespace Data { -std::vector<ChatRestrictions> ListOfRestrictions() { - using Flag = ChatRestriction; - - return { - Flag::SendMessages, - Flag::SendMedia, - Flag::SendStickers - | Flag::SendGifs - | Flag::SendGames - | Flag::SendInline, - Flag::EmbedLinks, - Flag::SendPolls, - Flag::InviteUsers, - Flag::PinMessages, - Flag::ChangeInfo, - }; -} - std::optional<QString> RestrictionError( not_null<PeerData*> peer, ChatRestriction restriction) { @@ -1275,22 +1257,4 @@ std::optional<int> ResolvePinnedCount( : std::nullopt; } -ChatAdminRights ChatAdminRightsFlags(const MTPChatAdminRights &rights) { - return rights.match([](const MTPDchatAdminRights &data) { - return ChatAdminRights::from_raw(int32(data.vflags().v)); - }); -} - -ChatRestrictions ChatBannedRightsFlags(const MTPChatBannedRights &rights) { - return rights.match([](const MTPDchatBannedRights &data) { - return ChatRestrictions::from_raw(int32(data.vflags().v)); - }); -} - -TimeId ChatBannedRightsUntilDate(const MTPChatBannedRights &rights) { - return rights.match([](const MTPDchatBannedRights &data) { - return data.vuntil_date().v; - }); -} - } // namespace Data diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 10e18379f..404c22884 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -17,75 +17,7 @@ class UserData; class ChatData; class ChannelData; -enum class ChatAdminRight { - ChangeInfo = (1 << 0), - PostMessages = (1 << 1), - EditMessages = (1 << 2), - DeleteMessages = (1 << 3), - BanUsers = (1 << 4), - InviteUsers = (1 << 5), - PinMessages = (1 << 7), - AddAdmins = (1 << 9), - Anonymous = (1 << 10), - ManageCall = (1 << 11), - Other = (1 << 12), -}; -inline constexpr bool is_flag_type(ChatAdminRight) { return true; } -using ChatAdminRights = base::flags<ChatAdminRight>; - -enum class ChatRestriction { - ViewMessages = (1 << 0), - SendMessages = (1 << 1), - SendMedia = (1 << 2), - SendStickers = (1 << 3), - SendGifs = (1 << 4), - SendGames = (1 << 5), - SendInline = (1 << 6), - EmbedLinks = (1 << 7), - SendPolls = (1 << 8), - ChangeInfo = (1 << 10), - InviteUsers = (1 << 15), - PinMessages = (1 << 17), -}; -inline constexpr bool is_flag_type(ChatRestriction) { return true; } -using ChatRestrictions = base::flags<ChatRestriction>; - -namespace Data { - -[[nodiscard]] ChatAdminRights ChatAdminRightsFlags( - const MTPChatAdminRights &rights); -[[nodiscard]] ChatRestrictions ChatBannedRightsFlags( - const MTPChatBannedRights &rights); -[[nodiscard]] TimeId ChatBannedRightsUntilDate( - const MTPChatBannedRights &rights); - -} // namespace Data - -struct ChatAdminRightsInfo { - ChatAdminRightsInfo() = default; - explicit ChatAdminRightsInfo(ChatAdminRights flags) : flags(flags) { - } - explicit ChatAdminRightsInfo(const MTPChatAdminRights &rights) - : flags(Data::ChatAdminRightsFlags(rights)) { - } - - ChatAdminRights flags; -}; - -struct ChatRestrictionsInfo { - ChatRestrictionsInfo() = default; - ChatRestrictionsInfo(ChatRestrictions flags, TimeId until) - : flags(flags) - , until(until) { - } - explicit ChatRestrictionsInfo(const MTPChatBannedRights &rights) - : flags(Data::ChatBannedRightsFlags(rights)) - , until(Data::ChatBannedRightsUntilDate(rights)) { - } - - ChatRestrictions flags; - TimeId until = 0; -}; +enum class ChatRestriction; struct BotCommand { QString command; @@ -528,8 +460,6 @@ private: namespace Data { -std::vector<ChatRestrictions> ListOfRestrictions(); - std::optional<QString> RestrictionError( not_null<PeerData*> peer, ChatRestriction restriction); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 3aa1bc2c9..e82966406 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/call_delayed.h" #include "core/file_utilities.h" #include "main/main_session.h" +#include "data/data_chat_participant_status.h" #include "data/data_session.h" #include "data/data_scheduled_messages.h" #include "data/data_user.h" diff --git a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp index 9d17f00b7..2b23004af 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_user.h" #include "data/data_changes.h" +#include "data/data_chat_participant_status.h" #include "inline_bots/inline_bot_result.h" #include "inline_bots/inline_bot_layout_item.h" #include "lang/lang_keys.h" diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm index 8bdbdff6d..ad42f4fbc 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/sandbox.h" #include "core/application.h" #include "core/core_settings.h" +#include "data/data_chat_participant_status.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_file_origin.h" From 3271cdf25136cf422030adf54a20fb4c57baac8b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 22 Nov 2021 00:16:44 +0300 Subject: [PATCH 088/180] Removed duplicated MTP* rights generation. --- .../boxes/peers/add_participants_box.cpp | 60 +------- .../boxes/peers/edit_participants_box.cpp | 144 ++++++++++-------- .../boxes/peers/edit_participants_box.h | 8 + 3 files changed, 93 insertions(+), 119 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 9c7aeb168..aa41ff240 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -658,37 +658,7 @@ void AddSpecialBoxController::editAdminDone( _editParticipantBox->closeBox(); } - const auto date = base::unixtime::now(); // Incorrect, but ignored. - if (_additional.isCreator(user) && user->isSelf()) { - using Flag = MTPDchannelParticipantCreator::Flag; - _additional.applyParticipant(MTP_channelParticipantCreator( - MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), - peerToBareMTPInt(user->id), - MTP_chatAdminRights( - MTP_flags(MTPDchatAdminRights::Flags::from_raw( - uint32(rights.flags)))), - MTP_string(rank))); - } else if (!rights.flags) { - _additional.applyParticipant(MTP_channelParticipant( - peerToBareMTPInt(user->id), - MTP_int(date))); - } else { - using Flag = MTPDchannelParticipantAdmin::Flag; - const auto alreadyPromotedBy = _additional.adminPromotedBy(user); - _additional.applyParticipant(MTP_channelParticipantAdmin( - MTP_flags(Flag::f_can_edit - | (rank.isEmpty() ? Flag(0) : Flag::f_rank)), - peerToBareMTPInt(user->id), - MTPlong(), // inviter_id - peerToBareMTPInt(alreadyPromotedBy - ? alreadyPromotedBy->id - : user->session().userPeerId()), - MTP_int(date), - MTP_chatAdminRights( - MTP_flags(MTPDchatAdminRights::Flags::from_raw( - uint32(rights.flags)))), - MTP_string(rank))); - } + _additional.applyAdminLocally(user, rights, rank); if (const auto callback = _adminDoneCallback) { callback(user, rights, rank); } @@ -765,33 +735,7 @@ void AddSpecialBoxController::editRestrictedDone( _editParticipantBox->closeBox(); } - const auto date = base::unixtime::now(); // Incorrect, but ignored. - if (!rights.flags) { - if (const auto user = participant->asUser()) { - _additional.applyParticipant(MTP_channelParticipant( - peerToBareMTPInt(user->id), - MTP_int(date))); - } else { - _additional.setExternal(participant); - } - } else { - const auto kicked = rights.flags & ChatRestriction::ViewMessages; - const auto alreadyRestrictedBy = _additional.restrictedBy( - participant); - _additional.applyParticipant(MTP_channelParticipantBanned( - MTP_flags(kicked - ? MTPDchannelParticipantBanned::Flag::f_left - : MTPDchannelParticipantBanned::Flag(0)), - peerToMTP(participant->id), - peerToBareMTPInt(alreadyRestrictedBy - ? alreadyRestrictedBy->id - : participant->session().userPeerId()), - MTP_int(date), - MTP_chatBannedRights( - MTP_flags(MTPDchatBannedRights::Flags::from_raw( - uint32(rights.flags))), - MTP_int(rights.until)))); - } + _additional.applyBannedLocally(participant, rights); if (const auto callback = _bannedDoneCallback) { callback(participant, rights); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index e4764ea0e..dcdb409b1 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -530,6 +530,76 @@ void ParticipantsAdditionalData::fillFromChannel( } } +void ParticipantsAdditionalData::applyAdminLocally( + UserData *user, + ChatAdminRightsInfo rights, + const QString &rank) { + const auto date = base::unixtime::now(); // Incorrect, but ignored. + if (isCreator(user) && user->isSelf()) { + using Flag = MTPDchannelParticipantCreator::Flag; + applyParticipant(MTP_channelParticipantCreator( + MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), + peerToBareMTPInt(user->id), + MTP_chatAdminRights( + MTP_flags(MTPDchatAdminRights::Flags::from_raw( + uint32(rights.flags)))), + MTP_string(rank))); + } else if (!rights.flags) { + applyParticipant(MTP_channelParticipant( + peerToBareMTPInt(user->id), + MTP_int(date))); + } else { + using Flag = MTPDchannelParticipantAdmin::Flag; + const auto alreadyPromotedBy = adminPromotedBy(user); + applyParticipant(MTP_channelParticipantAdmin( + MTP_flags(Flag::f_can_edit + | (rank.isEmpty() ? Flag(0) : Flag::f_rank)), + peerToBareMTPInt(user->id), + MTPlong(), // inviter_id + peerToBareMTPInt(alreadyPromotedBy + ? alreadyPromotedBy->id + : user->session().userPeerId()), + MTP_int(date), + MTP_chatAdminRights( + MTP_flags(MTPDchatAdminRights::Flags::from_raw( + uint32(rights.flags)))), + MTP_string(rank))); + } +} + +void ParticipantsAdditionalData::applyBannedLocally( + not_null<PeerData*> participant, + ChatRestrictionsInfo rights) { + const auto user = participant->asUser(); + const auto date = base::unixtime::now(); // Incorrect, but ignored. + if (!rights.flags) { + if (user) { + applyParticipant(MTP_channelParticipant( + peerToBareMTPInt(user->id), + MTP_int(date))); + } else { + setExternal(participant); + } + } else { + const auto kicked = rights.flags & ChatRestriction::ViewMessages; + const auto alreadyRestrictedBy = restrictedBy( + participant); + applyParticipant(MTP_channelParticipantBanned( + MTP_flags(kicked + ? MTPDchannelParticipantBanned::Flag::f_left + : MTPDchannelParticipantBanned::Flag(0)), + peerToMTP(participant->id), + peerToBareMTPInt(alreadyRestrictedBy + ? alreadyRestrictedBy->id + : participant->session().userPeerId()), + MTP_int(date), + MTP_chatBannedRights( + MTP_flags(MTPDchatBannedRights::Flags::from_raw( + uint32(rights.flags))), + MTP_int(rights.until)))); + } +} + PeerData *ParticipantsAdditionalData::applyParticipant( const MTPChannelParticipant &data) { return applyParticipant(data, _role); @@ -1558,43 +1628,18 @@ void ParticipantsBoxController::editAdminDone( _editParticipantBox->closeBox(); } - const auto date = base::unixtime::now(); // Incorrect, but ignored. - if (_additional.isCreator(user) && user->isSelf()) { - using Flag = MTPDchannelParticipantCreator::Flag; - _additional.applyParticipant(MTP_channelParticipantCreator( - MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), - peerToBareMTPInt(user->id), - MTP_chatAdminRights( - MTP_flags(MTPDchatAdminRights::Flags::from_raw( - uint32(rights.flags)))), - MTP_string(rank))); - } else if (!rights.flags) { - _additional.applyParticipant(MTP_channelParticipant( - peerToBareMTPInt(user->id), - MTP_int(date))); - if (_role == Role::Admins) { - removeRow(user); - } - } else { - using Flag = MTPDchannelParticipantAdmin::Flag; - const auto alreadyPromotedBy = _additional.adminPromotedBy(user); - _additional.applyParticipant(MTP_channelParticipantAdmin( - MTP_flags(Flag::f_can_edit - | (rank.isEmpty() ? Flag(0) : Flag::f_rank)), - peerToBareMTPInt(user->id), - MTPlong(), // inviter_id - peerToBareMTPInt(alreadyPromotedBy - ? alreadyPromotedBy->id - : user->session().userPeerId()), - MTP_int(date), - MTP_chatAdminRights( - MTP_flags(MTPDchatAdminRights::Flags::from_raw( - uint32(rights.flags)))), - MTP_string(rank))); - if (_role == Role::Admins) { - prependRow(user); - } else if (_role == Role::Kicked || _role == Role::Restricted) { - removeRow(user); + _additional.applyAdminLocally(user, rights, rank); + if (!_additional.isCreator(user) || !user->isSelf()) { + if (!rights.flags) { + if (_role == Role::Admins) { + removeRow(user); + } + } else { + if (_role == Role::Admins) { + prependRow(user); + } else if (_role == Role::Kicked || _role == Role::Restricted) { + removeRow(user); + } } } recomputeTypeFor(user); @@ -1636,36 +1681,13 @@ void ParticipantsBoxController::editRestrictedDone( _editParticipantBox->closeBox(); } - const auto user = participant->asUser(); - const auto date = base::unixtime::now(); // Incorrect, but ignored. + _additional.applyBannedLocally(participant, rights); if (!rights.flags) { - if (user) { - _additional.applyParticipant(MTP_channelParticipant( - peerToBareMTPInt(user->id), - MTP_int(date))); - } else { - _additional.setExternal(participant); - } if (_role == Role::Kicked || _role == Role::Restricted) { removeRow(participant); } } else { const auto kicked = rights.flags & ChatRestriction::ViewMessages; - const auto alreadyRestrictedBy = _additional.restrictedBy( - participant); - _additional.applyParticipant(MTP_channelParticipantBanned( - MTP_flags(kicked - ? MTPDchannelParticipantBanned::Flag::f_left - : MTPDchannelParticipantBanned::Flag(0)), - peerToMTP(participant->id), - peerToBareMTPInt(alreadyRestrictedBy - ? alreadyRestrictedBy->id - : participant->session().userPeerId()), - MTP_int(date), - MTP_chatBannedRights( - MTP_flags(MTPDchatBannedRights::Flags::from_raw( - uint32(rights.flags))), - MTP_int(rights.until)))); if (kicked) { if (_role == Role::Kicked) { prependRow(participant); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index c713a2ea9..9bafc2b53 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -108,6 +108,14 @@ public: void migrate(not_null<ChatData*> chat, not_null<ChannelData*> channel); + void applyAdminLocally( + UserData *user, + ChatAdminRightsInfo rights, + const QString &rank); + void applyBannedLocally( + not_null<PeerData*> participant, + ChatRestrictionsInfo rights); + private: UserData *applyCreator(const MTPDchannelParticipantCreator &data); UserData *applyAdmin(const MTPDchannelParticipantAdmin &data); From b9c64499bdce7ed4423e3613c6892d3e83fcac26 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 24 Nov 2021 07:25:05 +0300 Subject: [PATCH 089/180] Moved code for api participants to separated file. --- Telegram/CMakeLists.txt | 2 + .../SourceFiles/api/api_chat_participants.cpp | 659 ++++++++++++++++++ .../SourceFiles/api/api_chat_participants.h | 107 +++ Telegram/SourceFiles/api/api_invite_links.cpp | 3 +- Telegram/SourceFiles/api/api_updates.cpp | 5 +- Telegram/SourceFiles/apiwrap.cpp | 559 +-------------- Telegram/SourceFiles/apiwrap.h | 68 +- .../SourceFiles/boxes/delete_messages_box.cpp | 3 +- .../boxes/peer_list_controllers.cpp | 3 +- .../boxes/peers/add_participants_box.cpp | 7 +- .../boxes/peers/edit_participants_box.cpp | 22 +- .../group/calls_group_invite_controller.cpp | 3 +- .../calls/group/calls_group_panel.cpp | 7 +- .../chat_helpers/field_autocomplete.cpp | 7 +- Telegram/SourceFiles/data/data_channel.cpp | 73 -- Telegram/SourceFiles/data/data_channel.h | 4 - Telegram/SourceFiles/data/data_peer.cpp | 3 +- .../admin_log/history_admin_log_inner.cpp | 3 +- Telegram/SourceFiles/history/history.cpp | 3 +- .../SourceFiles/history/history_widget.cpp | 8 +- .../history_view_compose_controls.cpp | 3 +- .../view/history_view_top_bar_widget.cpp | 3 +- .../profile/profile_block_group_members.cpp | 10 +- .../SourceFiles/window/window_peer_menu.cpp | 5 +- 24 files changed, 840 insertions(+), 730 deletions(-) create mode 100644 Telegram/SourceFiles/api/api_chat_participants.cpp create mode 100644 Telegram/SourceFiles/api/api_chat_participants.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index df6bfd1b5..3dc2e6211 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -112,6 +112,8 @@ PRIVATE api/api_chat_filters.h api/api_chat_invite.cpp api/api_chat_invite.h + api/api_chat_participants.cpp + api/api_chat_participants.h api/api_cloud_password.cpp api/api_cloud_password.h api/api_common.h diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp new file mode 100644 index 000000000..5a6928513 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -0,0 +1,659 @@ +/* +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_chat_participants.h" + +#include "apiwrap.h" +#include "boxes/add_contact_box.h" // ShowAddParticipantsError +#include "data/data_changes.h" +#include "data/data_channel.h" +#include "data/data_channel_admins.h" +#include "data/data_chat.h" +#include "data/data_histories.h" +#include "data/data_session.h" +#include "data/data_user.h" +#include "history/history.h" +#include "main/main_session.h" +#include "mtproto/mtproto_config.h" + +namespace Api { +namespace { + +constexpr auto kSmallDelayMs = crl::time(5); + +// 1 second wait before reload members in channel after adding. +constexpr auto kReloadChannelMembersTimeout = 1000; + +// Max users in one super group invite request. +constexpr auto kMaxUsersPerInvite = 100; + +// How many messages from chat history server should forward to user, +// that was added to this chat. +constexpr auto kForwardMessagesOnAdd = 100; + +void ApplyMegagroupAdmins( + not_null<ChannelData*> channel, + const MTPDchannels_channelParticipants &data) { + Expects(channel->isMegagroup()); + + channel->owner().processUsers(data.vusers()); + + const auto &list = data.vparticipants().v; + const auto i = ranges::find( + list, + mtpc_channelParticipantCreator, + &MTPChannelParticipant::type); + if (i != list.end()) { + const auto &data = i->c_channelParticipantCreator(); + const auto userId = data.vuser_id().v; + channel->mgInfo->creator = channel->owner().userLoaded(userId); + channel->mgInfo->creatorRank = qs(data.vrank().value_or_empty()); + } else { + channel->mgInfo->creator = nullptr; + channel->mgInfo->creatorRank = QString(); + } + + auto adding = base::flat_map<UserId, QString>(); + auto admins = ranges::make_subrange( + list.begin(), list.end() + ) | ranges::views::transform([](const MTPChannelParticipant &p) { + const auto participantId = p.match([]( + const MTPDchannelParticipantBanned &data) { + return peerFromMTP(data.vpeer()); + }, [](const MTPDchannelParticipantLeft &data) { + return peerFromMTP(data.vpeer()); + }, [](const auto &data) { + return peerFromUser(data.vuser_id()); + }); + const auto rank = p.match([](const MTPDchannelParticipantAdmin &data) { + return qs(data.vrank().value_or_empty()); + }, [](const MTPDchannelParticipantCreator &data) { + return qs(data.vrank().value_or_empty()); + }, [](const auto &data) { + return QString(); + }); + return std::make_pair(participantId, rank); + }) | ranges::views::filter([](const auto &pair) { + return peerIsUser(pair.first); + }); + for (const auto &[participantId, rank] : admins) { + Assert(peerIsUser(participantId)); + adding.emplace(peerToUser(participantId), rank); + } + if (channel->mgInfo->creator) { + adding.emplace( + peerToUser(channel->mgInfo->creator->id), + channel->mgInfo->creatorRank); + } + auto removing = channel->mgInfo->admins; + if (removing.empty() && adding.empty()) { + // Add some admin-placeholder so we don't DDOS + // server with admins list requests. + LOG(("API Error: Got empty admins list from server.")); + adding.emplace(0, QString()); + } + + Data::ChannelAdminChanges changes(channel); + for (const auto &[addingId, rank] : adding) { + if (!removing.remove(addingId)) { + changes.add(addingId, rank); + } + } + for (const auto &[removingId, rank] : removing) { + changes.remove(removingId); + } +} + +} // namespace + +ChatParticipants::ChatParticipants(not_null<ApiWrap*> api) +: _session(&api->session()) +, _api(&api->instance()) { +} + +void ChatParticipants::requestForAdd( + not_null<ChannelData*> channel, + Fn<void(const MTPchannels_ChannelParticipants&)> callback) { + _channelMembersForAddCallback = std::move(callback); + if (_channelMembersForAdd == channel) { + return; + } + _api.request(base::take(_channelMembersForAddRequestId)).cancel(); + + const auto offset = 0; + const auto participantsHash = uint64(0); + + _channelMembersForAdd = channel; + _channelMembersForAddRequestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsRecent(), + MTP_int(offset), + MTP_int(_session->serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + base::take(_channelMembersForAddRequestId); + base::take(_channelMembersForAdd); + base::take(_channelMembersForAddCallback)(result); + }).fail([=](const MTP::Error &error) { + base::take(_channelMembersForAddRequestId); + base::take(_channelMembersForAdd); + base::take(_channelMembersForAddCallback); + }).send(); +} + +void ChatParticipants::refreshChannelAdmins( + not_null<ChannelData*> channel, + const QVector<MTPChannelParticipant> &participants) { + Data::ChannelAdminChanges changes(channel); + for (const auto &p : participants) { + const auto participantId = p.match([]( + const MTPDchannelParticipantBanned &data) { + return peerFromMTP(data.vpeer()); + }, [](const MTPDchannelParticipantLeft &data) { + return peerFromMTP(data.vpeer()); + }, [](const auto &data) { + return peerFromUser(data.vuser_id()); + }); + const auto userId = peerToUser(participantId); + p.match([&](const MTPDchannelParticipantAdmin &data) { + Assert(peerIsUser(participantId)); + changes.add(userId, qs(data.vrank().value_or_empty())); + }, [&](const MTPDchannelParticipantCreator &data) { + Assert(peerIsUser(participantId)); + const auto rank = qs(data.vrank().value_or_empty()); + if (const auto info = channel->mgInfo.get()) { + info->creator = channel->owner().userLoaded(userId); + info->creatorRank = rank; + } + changes.add(userId, rank); + }, [&](const auto &data) { + if (userId) { + changes.remove(userId); + } + }); + } +} + +void ChatParticipants::requestLast(not_null<ChannelData*> channel) { + if (!channel->isMegagroup() + || _participantsRequests.contains(channel)) { + return; + } + + const auto offset = 0; + const auto participantsHash = uint64(0); + const auto requestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsRecent(), + MTP_int(offset), + MTP_int(_session->serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + _participantsRequests.remove(channel); + parse(channel, result, [&]( + int availableCount, + const QVector<MTPChannelParticipant> &list) { + applyLastList( + channel, + availableCount, + list); + }); + }).fail([this, channel](const MTP::Error &error) { + _participantsRequests.remove(channel); + }).send(); + + _participantsRequests[channel] = requestId; +} + +void ChatParticipants::requestBots(not_null<ChannelData*> channel) { + if (!channel->isMegagroup() || _botsRequests.contains(channel)) { + return; + } + + const auto offset = 0; + const auto participantsHash = uint64(0); + const auto requestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsBots(), + MTP_int(offset), + MTP_int(_session->serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + _botsRequests.remove(channel); + parse(channel, result, [&]( + int availableCount, + const QVector<MTPChannelParticipant> &list) { + applyBotsList( + channel, + availableCount, + list); + }); + }).fail([=](const MTP::Error &error) { + _botsRequests.remove(channel); + }).send(); + + _botsRequests[channel] = requestId; +} + +void ChatParticipants::requestAdmins(not_null<ChannelData*> channel) { + if (!channel->isMegagroup() || _adminsRequests.contains(channel)) { + return; + } + + const auto offset = 0; + const auto participantsHash = uint64(0); + const auto requestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsAdmins(), + MTP_int(offset), + MTP_int(_session->serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + _adminsRequests.remove(channel); + result.match([&](const MTPDchannels_channelParticipants &data) { + ApplyMegagroupAdmins(channel, data); + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: channels.channelParticipantsNotModified received!")); + }); + }).fail([=](const MTP::Error &error) { + _adminsRequests.remove(channel); + }).send(); + + _adminsRequests[channel] = requestId; +} + +void ChatParticipants::requestCountDelayed( + not_null<ChannelData*> channel) { + _participantsCountRequestTimer.call( + kReloadChannelMembersTimeout, + [=] { channel->updateFullForced(); }); +} + +void ChatParticipants::add( + not_null<PeerData*> peer, + const std::vector<not_null<UserData*>> &users, + Fn<void(bool)> done) { + if (const auto chat = peer->asChat()) { + for (const auto &user : users) { + _api.request(MTPmessages_AddChatUser( + chat->inputChat, + user->inputUser, + MTP_int(kForwardMessagesOnAdd) + )).done([=](const MTPUpdates &result) { + _session->api().applyUpdates(result); + if (done) done(true); + }).fail([=](const MTP::Error &error) { + ShowAddParticipantsError(error.type(), peer, { 1, user }); + if (done) done(false); + }).afterDelay(kSmallDelayMs).send(); + } + } else if (const auto channel = peer->asChannel()) { + const auto hasBot = ranges::any_of(users, &UserData::isBot); + if (!peer->isMegagroup() && hasBot) { + ShowAddParticipantsError("USER_BOT", peer, users); + return; + } + auto list = QVector<MTPInputUser>(); + list.reserve(std::min(int(users.size()), int(kMaxUsersPerInvite))); + const auto send = [&] { + const auto callback = base::take(done); + _api.request(MTPchannels_InviteToChannel( + channel->inputChannel, + MTP_vector<MTPInputUser>(list) + )).done([=](const MTPUpdates &result) { + _session->api().applyUpdates(result); + requestCountDelayed(channel); + if (callback) callback(true); + }).fail([=](const MTP::Error &error) { + ShowAddParticipantsError(error.type(), peer, users); + if (callback) callback(false); + }).afterDelay(kSmallDelayMs).send(); + }; + for (const auto &user : users) { + list.push_back(user->inputUser); + if (list.size() == kMaxUsersPerInvite) { + send(); + list.clear(); + } + } + if (!list.empty()) { + send(); + } + } else { + Unexpected("User in ChatParticipants::add."); + } +} + +void ChatParticipants::parseRecent( + not_null<ChannelData*> channel, + const MTPchannels_ChannelParticipants &result, + Fn<void( + int availableCount, + const QVector<MTPChannelParticipant> &list)> callbackList, + Fn<void()> callbackNotModified) { + parse(channel, result, [&]( + int availableCount, + const QVector<MTPChannelParticipant> &list) { + auto applyLast = channel->isMegagroup() + && (channel->mgInfo->lastParticipants.size() <= list.size()); + if (applyLast) { + applyLastList( + channel, + availableCount, + list); + } + if (callbackList) { + callbackList(availableCount, list); + } + }, std::move(callbackNotModified)); +} + +void ChatParticipants::parse( + not_null<ChannelData*> channel, + const MTPchannels_ChannelParticipants &result, + Fn<void( + int availableCount, + const QVector<MTPChannelParticipant> &list)> callbackList, + Fn<void()> callbackNotModified) { + result.match([&](const MTPDchannels_channelParticipants &data) { + _session->data().processUsers(data.vusers()); + if (channel->mgInfo) { + refreshChannelAdmins(channel, data.vparticipants().v); + } + if (callbackList) { + callbackList(data.vcount().v, data.vparticipants().v); + } + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + if (callbackNotModified) { + callbackNotModified(); + } else { + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); + } + }); +} + +void ChatParticipants::applyLastList( + not_null<ChannelData*> channel, + int availableCount, + const QVector<MTPChannelParticipant> &list) { + channel->mgInfo->lastAdmins.clear(); + channel->mgInfo->lastRestricted.clear(); + channel->mgInfo->lastParticipants.clear(); + channel->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate + | MegagroupInfo::LastParticipantsOnceReceived; + + auto botStatus = channel->mgInfo->botStatus; + for (const auto &p : list) { + const auto participantId = p.match([]( + const MTPDchannelParticipantBanned &data) { + return peerFromMTP(data.vpeer()); + }, [](const MTPDchannelParticipantLeft &data) { + return peerFromMTP(data.vpeer()); + }, [](const auto &data) { + return peerFromUser(data.vuser_id()); + }); + if (!participantId) { + continue; + } + const auto participant = _session->data().peer(participantId); + const auto user = participant->asUser(); + const auto adminCanEdit = (p.type() == mtpc_channelParticipantAdmin) + ? p.c_channelParticipantAdmin().is_can_edit() + : (p.type() == mtpc_channelParticipantCreator) + ? channel->amCreator() + : false; + const auto adminRights = (p.type() == mtpc_channelParticipantAdmin) + ? ChatAdminRightsInfo(p.c_channelParticipantAdmin().vadmin_rights()) + : (p.type() == mtpc_channelParticipantCreator) + ? ChatAdminRightsInfo(p.c_channelParticipantCreator().vadmin_rights()) + : ChatAdminRightsInfo(); + const auto restrictedRights = (p.type() == mtpc_channelParticipantBanned) + ? ChatRestrictionsInfo( + p.c_channelParticipantBanned().vbanned_rights()) + : ChatRestrictionsInfo(); + if (p.type() == mtpc_channelParticipantCreator) { + Assert(user != nullptr); + const auto &creator = p.c_channelParticipantCreator(); + const auto rank = qs(creator.vrank().value_or_empty()); + channel->mgInfo->creator = user; + channel->mgInfo->creatorRank = rank; + if (!channel->mgInfo->admins.empty()) { + Data::ChannelAdminChanges(channel).add( + peerToUser(participantId), + rank); + } + } + if (user + && !base::contains(channel->mgInfo->lastParticipants, user)) { + channel->mgInfo->lastParticipants.push_back(user); + if (adminRights.flags) { + channel->mgInfo->lastAdmins.emplace( + user, + MegagroupInfo::Admin{ adminRights, adminCanEdit }); + } else if (restrictedRights.flags) { + channel->mgInfo->lastRestricted.emplace( + user, + MegagroupInfo::Restricted{ restrictedRights }); + } + if (user->isBot()) { + channel->mgInfo->bots.insert(user); + if (channel->mgInfo->botStatus != 0 && channel->mgInfo->botStatus < 2) { + channel->mgInfo->botStatus = 2; + } + } + } + } + // + // getParticipants(Recent) sometimes can't return all members, + // only some last subset, size of this subset is availableCount. + // + // So both list size and availableCount have nothing to do with + // the full supergroup members count. + // + //if (list.isEmpty()) { + // channel->setMembersCount(channel->mgInfo->lastParticipants.size()); + //} else { + // channel->setMembersCount(availableCount); + //} + _session->changes().peerUpdated( + channel, + (Data::PeerUpdate::Flag::Members | Data::PeerUpdate::Flag::Admins)); + + channel->mgInfo->botStatus = botStatus; + _session->changes().peerUpdated( + channel, + Data::PeerUpdate::Flag::FullInfo); +} + +void ChatParticipants::applyBotsList( + not_null<ChannelData*> channel, + int availableCount, + const QVector<MTPChannelParticipant> &list) { + const auto history = _session->data().historyLoaded(channel); + channel->mgInfo->bots.clear(); + channel->mgInfo->botStatus = -1; + + auto needBotsInfos = false; + auto botStatus = channel->mgInfo->botStatus; + auto keyboardBotFound = !history || !history->lastKeyboardFrom; + for (const auto &p : list) { + const auto participantId = p.match([]( + const MTPDchannelParticipantBanned &data) { + return peerFromMTP(data.vpeer()); + }, [](const MTPDchannelParticipantLeft &data) { + return peerFromMTP(data.vpeer()); + }, [](const auto &data) { + return peerFromUser(data.vuser_id()); + }); + if (!participantId) { + continue; + } + + const auto participant = _session->data().peer(participantId); + const auto user = participant->asUser(); + if (user && user->isBot()) { + channel->mgInfo->bots.insert(user); + botStatus = 2;// (botStatus > 0/* || !i.key()->botInfo->readsAllHistory*/) ? 2 : 1; + if (!user->botInfo->inited) { + needBotsInfos = true; + } + } + if (!keyboardBotFound + && participant->id == history->lastKeyboardFrom) { + keyboardBotFound = true; + } + } + if (needBotsInfos) { + _session->api().requestFullPeer(channel); + } + if (!keyboardBotFound) { + history->clearLastKeyboard(); + } + + channel->mgInfo->botStatus = botStatus; + _session->changes().peerUpdated( + channel, + Data::PeerUpdate::Flag::FullInfo); +} + +void ChatParticipants::requestSelf(not_null<ChannelData*> channel) { + if (_selfParticipantRequests.contains(channel)) { + return; + } + + const auto finalize = [=]( + UserId inviter = -1, + TimeId inviteDate = 0, + bool inviteViaRequest = false) { + channel->inviter = inviter; + channel->inviteDate = inviteDate; + channel->inviteViaRequest = inviteViaRequest; + if (const auto history = _session->data().historyLoaded(channel)) { + if (history->lastMessageKnown()) { + history->checkLocalMessages(); + history->owner().sendHistoryChangeNotifications(); + } else { + history->owner().histories().requestDialogEntry(history); + } + } + }; + _selfParticipantRequests.emplace(channel); + _api.request(MTPchannels_GetParticipant( + channel->inputChannel, + MTP_inputPeerSelf() + )).done([=](const MTPchannels_ChannelParticipant &result) { + _selfParticipantRequests.erase(channel); + result.match([&](const MTPDchannels_channelParticipant &data) { + _session->data().processUsers(data.vusers()); + + const auto &participant = data.vparticipant(); + participant.match([&](const MTPDchannelParticipantSelf &data) { + finalize( + data.vinviter_id().v, + data.vdate().v, + data.is_via_request()); + }, [&](const MTPDchannelParticipantCreator &) { + if (channel->mgInfo) { + channel->mgInfo->creator = _session->user(); + } + finalize(_session->userId(), channel->date); + }, [&](const MTPDchannelParticipantAdmin &data) { + const auto inviter = data.is_self() + ? data.vinviter_id().value_or(-1) + : -1; + finalize(inviter, data.vdate().v); + }, [&](const MTPDchannelParticipantBanned &data) { + LOG(("API Error: Got self banned participant.")); + finalize(); + }, [&](const MTPDchannelParticipant &data) { + LOG(("API Error: Got self regular participant.")); + finalize(); + }, [&](const MTPDchannelParticipantLeft &data) { + LOG(("API Error: Got self left participant.")); + finalize(); + }); + }); + }).fail([=](const MTP::Error &error) { + _selfParticipantRequests.erase(channel); + if (error.type() == qstr("CHANNEL_PRIVATE")) { + channel->privateErrorReceived(); + } + finalize(); + }).afterDelay(kSmallDelayMs).send(); +} + +void ChatParticipants::kick( + not_null<ChatData*> chat, + not_null<PeerData*> participant) { + Expects(participant->isUser()); + + _api.request(MTPmessages_DeleteChatUser( + MTP_flags(0), + chat->inputChat, + participant->asUser()->inputUser + )).done([=](const MTPUpdates &result) { + _session->api().applyUpdates(result); + }).send(); +} + +void ChatParticipants::kick( + not_null<ChannelData*> channel, + not_null<PeerData*> participant, + ChatRestrictionsInfo currentRights) { + const auto kick = KickRequest(channel, participant); + if (_kickRequests.contains(kick)) return; + + const auto rights = ChannelData::KickedRestrictedRights(participant); + const auto requestId = _api.request(MTPchannels_EditBanned( + channel->inputChannel, + participant->input, + MTP_chatBannedRights( + MTP_flags( + MTPDchatBannedRights::Flags::from_raw(uint32(rights.flags))), + MTP_int(rights.until)) + )).done([=](const MTPUpdates &result) { + _session->api().applyUpdates(result); + + _kickRequests.remove(KickRequest(channel, participant)); + channel->applyEditBanned(participant, currentRights, rights); + }).fail([this, kick](const MTP::Error &error) { + _kickRequests.remove(kick); + }).send(); + + _kickRequests.emplace(kick, requestId); +} + +void ChatParticipants::unblock( + not_null<ChannelData*> channel, + not_null<PeerData*> participant) { + const auto kick = KickRequest(channel, participant); + if (_kickRequests.contains(kick)) { + return; + } + + const auto requestId = _api.request(MTPchannels_EditBanned( + channel->inputChannel, + participant->input, + MTP_chatBannedRights(MTP_flags(0), MTP_int(0)) + )).done([=](const MTPUpdates &result) { + _session->api().applyUpdates(result); + + _kickRequests.remove(KickRequest(channel, participant)); + if (channel->kickedCount() > 0) { + channel->setKickedCount(channel->kickedCount() - 1); + } else { + channel->updateFullForced(); + } + }).fail([=](const MTP::Error &error) { + _kickRequests.remove(kick); + }).send(); + + _kickRequests.emplace(kick, requestId); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_participants.h b/Telegram/SourceFiles/api/api_chat_participants.h new file mode 100644 index 000000000..6794c4db2 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_participants.h @@ -0,0 +1,107 @@ +/* +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" +#include "base/timer.h" + +class ApiWrap; +class ChannelData; + +struct ChatRestrictionsInfo; + +namespace Main { +class Session; +} // namespace Main + +namespace Api { + +class ChatParticipants final { +public: + explicit ChatParticipants(not_null<ApiWrap*> api); + + void requestLast(not_null<ChannelData*> channel); + void requestBots(not_null<ChannelData*> channel); + void requestAdmins(not_null<ChannelData*> channel); + void requestCountDelayed(not_null<ChannelData*> channel); + + void parse( + not_null<ChannelData*> channel, + const MTPchannels_ChannelParticipants &result, + Fn<void( + int availableCount, + const QVector<MTPChannelParticipant> &list)> callbackList, + Fn<void()> callbackNotModified = nullptr); + void parseRecent( + not_null<ChannelData*> channel, + const MTPchannels_ChannelParticipants &result, + Fn<void( + int availableCount, + const QVector<MTPChannelParticipant> &list)> callbackList = nullptr, + Fn<void()> callbackNotModified = nullptr); + void add( + not_null<PeerData*> peer, + const std::vector<not_null<UserData*>> &users, + Fn<void(bool)> done = nullptr); + + void requestSelf(not_null<ChannelData*> channel); + + void requestForAdd( + not_null<ChannelData*> channel, + Fn<void(const MTPchannels_ChannelParticipants&)> callback); + + void kick( + not_null<ChatData*> chat, + not_null<PeerData*> participant); + void kick( + not_null<ChannelData*> channel, + not_null<PeerData*> participant, + ChatRestrictionsInfo currentRights); + void unblock( + not_null<ChannelData*> channel, + not_null<PeerData*> participant); + +private: + void applyLastList( + not_null<ChannelData*> channel, + int availableCount, + const QVector<MTPChannelParticipant> &list); + void applyBotsList( + not_null<ChannelData*> channel, + int availableCount, + const QVector<MTPChannelParticipant> &list); + + void refreshChannelAdmins( + not_null<ChannelData*> channel, + const QVector<MTPChannelParticipant> &participants); + + const not_null<Main::Session*> _session; + MTP::Sender _api; + + using PeerRequests = base::flat_map<PeerData*, mtpRequestId>; + + PeerRequests _participantsRequests; + PeerRequests _botsRequests; + PeerRequests _adminsRequests; + base::DelayedCallTimer _participantsCountRequestTimer; + + ChannelData *_channelMembersForAdd = nullptr; + mtpRequestId _channelMembersForAddRequestId = 0; + Fn<void( + const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback; + + base::flat_set<not_null<ChannelData*>> _selfParticipantRequests; + + using KickRequest = std::pair< + not_null<ChannelData*>, + not_null<PeerData*>>; + base::flat_map<KickRequest, mtpRequestId> _kickRequests; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index d3d1d4c85..d53c3857e 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "api/api_invite_links.h" +#include "api/api_chat_participants.h" #include "data/data_peer.h" #include "data/data_user.h" #include "data/data_chat.h" @@ -478,7 +479,7 @@ void InviteLinks::processRequest( ++chat->count; } } else if (const auto channel = peer->asChannel()) { - _api->requestParticipantsCountDelayed(channel); + _api->chatParticipants().requestCountDelayed(channel); } _api->applyUpdates(result); if (link.isEmpty() && approved) { diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 68701250f..ffefc85de 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_updates.h" #include "api/api_authorizations.h" +#include "api/api_chat_participants.h" #include "api/api_text_entities.h" #include "api/api_user_privacy.h" #include "main/main_session.h" @@ -1498,7 +1499,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { if (channel->mgInfo->lastParticipants.size() < _session->serverConfig().chatSizeMax && (channel->mgInfo->lastParticipants.empty() || channel->mgInfo->lastParticipants.size() < channel->membersCount())) { - session().api().requestLastParticipants(channel); + session().api().chatParticipants().requestLast(channel); } } @@ -2127,7 +2128,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { history->owner().histories().requestDialogEntry(history); } if (!channel->amCreator()) { - session().api().requestSelfParticipant(channel); + session().api().chatParticipants().requestSelf(channel); } } } diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 0f2af9379..d5936b946 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -10,6 +10,7 @@ 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_chat_participants.h" #include "api/api_cloud_password.h" #include "api/api_hash.h" #include "api/api_invite_links.h" @@ -89,19 +90,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { -// 1 second wait before reload members in channel after adding. -constexpr auto kReloadChannelMembersTimeout = 1000; - // Save draft to the cloud with 1 sec extra delay. constexpr auto kSaveCloudDraftTimeout = 1000; -// Max users in one super group invite request. -constexpr auto kMaxUsersPerInvite = 100; - -// How many messages from chat history server should forward to user, -// that was added to this chat. -constexpr auto kForwardMessagesOnAdd = 100; - constexpr auto kTopPromotionInterval = TimeId(60 * 60); constexpr auto kTopPromotionMinDelay = TimeId(10); constexpr auto kSmallDelayMs = 5; @@ -150,7 +141,8 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session) , _views(std::make_unique<Api::ViewsManager>(this)) , _confirmPhone(std::make_unique<Api::ConfirmPhone>(this)) , _peerPhoto(std::make_unique<Api::PeerPhoto>(this)) -, _polls(std::make_unique<Api::Polls>(this)) { +, _polls(std::make_unique<Api::Polls>(this)) +, _chatParticipants(std::make_unique<Api::ChatParticipants>(this)) { crl::on_main(session, [=] { // You can't use _session->lifetime() in the constructor, // only queued, because it is not constructed yet. @@ -1390,373 +1382,6 @@ void ApiWrap::requestPeers(const QList<PeerData*> &peers) { } } -void ApiWrap::requestLastParticipants(not_null<ChannelData*> channel) { - if (!channel->isMegagroup() - || _participantsRequests.contains(channel)) { - return; - } - - const auto offset = 0; - const auto participantsHash = uint64(0); - const auto requestId = request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsRecent(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - _participantsRequests.remove(channel); - parseChannelParticipants(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - applyLastParticipantsList( - channel, - availableCount, - list); - }); - }).fail([this, channel](const MTP::Error &error) { - _participantsRequests.remove(channel); - }).send(); - - _participantsRequests.insert(channel, requestId); -} - -void ApiWrap::requestBots(not_null<ChannelData*> channel) { - if (!channel->isMegagroup() || _botsRequests.contains(channel)) { - return; - } - - const auto offset = 0; - const auto participantsHash = uint64(0); - const auto requestId = request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsBots(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - _botsRequests.remove(channel); - parseChannelParticipants(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - applyBotsList( - channel, - availableCount, - list); - }); - }).fail([=](const MTP::Error &error) { - _botsRequests.remove(channel); - }).send(); - - _botsRequests.insert(channel, requestId); -} - -void ApiWrap::requestAdmins(not_null<ChannelData*> channel) { - if (!channel->isMegagroup() || _adminsRequests.contains(channel)) { - return; - } - - const auto offset = 0; - const auto participantsHash = uint64(0); - const auto requestId = request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsAdmins(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - _adminsRequests.remove(channel); - result.match([&](const MTPDchannels_channelParticipants &data) { - Data::ApplyMegagroupAdmins(channel, data); - }, [&](const MTPDchannels_channelParticipantsNotModified &) { - LOG(("API Error: channels.channelParticipantsNotModified received!")); - }); - }).fail([=](const MTP::Error &error) { - _adminsRequests.remove(channel); - }).send(); - - _adminsRequests.insert(channel, requestId); -} - -void ApiWrap::applyLastParticipantsList( - not_null<ChannelData*> channel, - int availableCount, - const QVector<MTPChannelParticipant> &list) { - channel->mgInfo->lastAdmins.clear(); - channel->mgInfo->lastRestricted.clear(); - channel->mgInfo->lastParticipants.clear(); - channel->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate - | MegagroupInfo::LastParticipantsOnceReceived; - - auto botStatus = channel->mgInfo->botStatus; - for (const auto &p : list) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); - if (!participantId) { - continue; - } - const auto participant = _session->data().peer(participantId); - const auto user = participant->asUser(); - const auto adminCanEdit = (p.type() == mtpc_channelParticipantAdmin) - ? p.c_channelParticipantAdmin().is_can_edit() - : (p.type() == mtpc_channelParticipantCreator) - ? channel->amCreator() - : false; - const auto adminRights = (p.type() == mtpc_channelParticipantAdmin) - ? ChatAdminRightsInfo(p.c_channelParticipantAdmin().vadmin_rights()) - : (p.type() == mtpc_channelParticipantCreator) - ? ChatAdminRightsInfo(p.c_channelParticipantCreator().vadmin_rights()) - : ChatAdminRightsInfo(); - const auto restrictedRights = (p.type() == mtpc_channelParticipantBanned) - ? ChatRestrictionsInfo( - p.c_channelParticipantBanned().vbanned_rights()) - : ChatRestrictionsInfo(); - if (p.type() == mtpc_channelParticipantCreator) { - Assert(user != nullptr); - const auto &creator = p.c_channelParticipantCreator(); - const auto rank = qs(creator.vrank().value_or_empty()); - channel->mgInfo->creator = user; - channel->mgInfo->creatorRank = rank; - if (!channel->mgInfo->admins.empty()) { - Data::ChannelAdminChanges(channel).add( - peerToUser(participantId), - rank); - } - } - if (user - && !base::contains(channel->mgInfo->lastParticipants, user)) { - channel->mgInfo->lastParticipants.push_back(user); - if (adminRights.flags) { - channel->mgInfo->lastAdmins.emplace( - user, - MegagroupInfo::Admin{ adminRights, adminCanEdit }); - } else if (restrictedRights.flags) { - channel->mgInfo->lastRestricted.emplace( - user, - MegagroupInfo::Restricted{ restrictedRights }); - } - if (user->isBot()) { - channel->mgInfo->bots.insert(user); - if (channel->mgInfo->botStatus != 0 && channel->mgInfo->botStatus < 2) { - channel->mgInfo->botStatus = 2; - } - } - } - } - // - // getParticipants(Recent) sometimes can't return all members, - // only some last subset, size of this subset is availableCount. - // - // So both list size and availableCount have nothing to do with - // the full supergroup members count. - // - //if (list.isEmpty()) { - // channel->setMembersCount(channel->mgInfo->lastParticipants.size()); - //} else { - // channel->setMembersCount(availableCount); - //} - session().changes().peerUpdated( - channel, - (Data::PeerUpdate::Flag::Members | Data::PeerUpdate::Flag::Admins)); - - channel->mgInfo->botStatus = botStatus; - _session->changes().peerUpdated( - channel, - Data::PeerUpdate::Flag::FullInfo); -} - -void ApiWrap::applyBotsList( - not_null<ChannelData*> channel, - int availableCount, - const QVector<MTPChannelParticipant> &list) { - const auto history = _session->data().historyLoaded(channel); - channel->mgInfo->bots.clear(); - channel->mgInfo->botStatus = -1; - - auto needBotsInfos = false; - auto botStatus = channel->mgInfo->botStatus; - auto keyboardBotFound = !history || !history->lastKeyboardFrom; - for (const auto &p : list) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); - if (!participantId) { - continue; - } - - const auto participant = _session->data().peer(participantId); - const auto user = participant->asUser(); - if (user && user->isBot()) { - channel->mgInfo->bots.insert(user); - botStatus = 2;// (botStatus > 0/* || !i.key()->botInfo->readsAllHistory*/) ? 2 : 1; - if (!user->botInfo->inited) { - needBotsInfos = true; - } - } - if (!keyboardBotFound - && participant->id == history->lastKeyboardFrom) { - keyboardBotFound = true; - } - } - if (needBotsInfos) { - requestFullPeer(channel); - } - if (!keyboardBotFound) { - history->clearLastKeyboard(); - } - - channel->mgInfo->botStatus = botStatus; - _session->changes().peerUpdated( - channel, - Data::PeerUpdate::Flag::FullInfo); -} - -void ApiWrap::requestSelfParticipant(not_null<ChannelData*> channel) { - if (_selfParticipantRequests.contains(channel)) { - return; - } - - const auto finalize = [=]( - UserId inviter = -1, - TimeId inviteDate = 0, - bool inviteViaRequest = false) { - channel->inviter = inviter; - channel->inviteDate = inviteDate; - channel->inviteViaRequest = inviteViaRequest; - if (const auto history = _session->data().historyLoaded(channel)) { - if (history->lastMessageKnown()) { - history->checkLocalMessages(); - history->owner().sendHistoryChangeNotifications(); - } else { - history->owner().histories().requestDialogEntry(history); - } - } - }; - _selfParticipantRequests.emplace(channel); - request(MTPchannels_GetParticipant( - channel->inputChannel, - MTP_inputPeerSelf() - )).done([=](const MTPchannels_ChannelParticipant &result) { - _selfParticipantRequests.erase(channel); - result.match([&](const MTPDchannels_channelParticipant &data) { - _session->data().processUsers(data.vusers()); - - const auto &participant = data.vparticipant(); - participant.match([&](const MTPDchannelParticipantSelf &data) { - finalize( - data.vinviter_id().v, - data.vdate().v, - data.is_via_request()); - }, [&](const MTPDchannelParticipantCreator &) { - if (channel->mgInfo) { - channel->mgInfo->creator = _session->user(); - } - finalize(_session->userId(), channel->date); - }, [&](const MTPDchannelParticipantAdmin &data) { - const auto inviter = data.is_self() - ? data.vinviter_id().value_or(-1) - : -1; - finalize(inviter, data.vdate().v); - }, [&](const MTPDchannelParticipantBanned &data) { - LOG(("API Error: Got self banned participant.")); - finalize(); - }, [&](const MTPDchannelParticipant &data) { - LOG(("API Error: Got self regular participant.")); - finalize(); - }, [&](const MTPDchannelParticipantLeft &data) { - LOG(("API Error: Got self left participant.")); - finalize(); - }); - }); - }).fail([=](const MTP::Error &error) { - _selfParticipantRequests.erase(channel); - if (error.type() == qstr("CHANNEL_PRIVATE")) { - channel->privateErrorReceived(); - } - finalize(); - }).afterDelay(kSmallDelayMs).send(); -} - -void ApiWrap::kickParticipant( - not_null<ChatData*> chat, - not_null<PeerData*> participant) { - Expects(participant->isUser()); - - request(MTPmessages_DeleteChatUser( - MTP_flags(0), - chat->inputChat, - participant->asUser()->inputUser - )).done([=](const MTPUpdates &result) { - applyUpdates(result); - }).send(); -} - -void ApiWrap::kickParticipant( - not_null<ChannelData*> channel, - not_null<PeerData*> participant, - ChatRestrictionsInfo currentRights) { - const auto kick = KickRequest(channel, participant); - if (_kickRequests.contains(kick)) return; - - const auto rights = ChannelData::KickedRestrictedRights(participant); - const auto requestId = request(MTPchannels_EditBanned( - channel->inputChannel, - participant->input, - MTP_chatBannedRights( - MTP_flags( - MTPDchatBannedRights::Flags::from_raw(uint32(rights.flags))), - MTP_int(rights.until)) - )).done([=](const MTPUpdates &result) { - applyUpdates(result); - - _kickRequests.remove(KickRequest(channel, participant)); - channel->applyEditBanned(participant, currentRights, rights); - }).fail([this, kick](const MTP::Error &error) { - _kickRequests.remove(kick); - }).send(); - - _kickRequests.emplace(kick, requestId); -} - -void ApiWrap::unblockParticipant( - not_null<ChannelData*> channel, - not_null<PeerData*> participant) { - const auto kick = KickRequest(channel, participant); - if (_kickRequests.contains(kick)) { - return; - } - - const auto requestId = request(MTPchannels_EditBanned( - channel->inputChannel, - participant->input, - MTP_chatBannedRights(MTP_flags(0), MTP_int(0)) - )).done([=](const MTPUpdates &result) { - applyUpdates(result); - - _kickRequests.remove(KickRequest(channel, participant)); - if (channel->kickedCount() > 0) { - channel->setKickedCount(channel->kickedCount() - 1); - } else { - channel->updateFullForced(); - } - }).fail([=](const MTP::Error &error) { - _kickRequests.remove(kick); - }).send(); - - _kickRequests.emplace(kick, requestId); -} - void ApiWrap::deleteAllFromParticipant( not_null<ChannelData*> channel, not_null<PeerData*> from) { @@ -1792,36 +1417,6 @@ void ApiWrap::deleteAllFromParticipantSend( }).send(); } -void ApiWrap::requestChannelMembersForAdd( - not_null<ChannelData*> channel, - Fn<void(const MTPchannels_ChannelParticipants&)> callback) { - _channelMembersForAddCallback = std::move(callback); - if (_channelMembersForAdd == channel) { - return; - } - request(base::take(_channelMembersForAddRequestId)).cancel(); - - const auto offset = 0; - const auto participantsHash = uint64(0); - - _channelMembersForAdd = channel; - _channelMembersForAddRequestId = request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsRecent(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - base::take(_channelMembersForAddRequestId); - base::take(_channelMembersForAdd); - base::take(_channelMembersForAddCallback)(result); - }).fail([=](const MTP::Error &error) { - base::take(_channelMembersForAddRequestId); - base::take(_channelMembersForAdd); - base::take(_channelMembersForAddCallback); - }).send(); -} - void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) { if (!_stickerSetRequests.contains(setId)) { _stickerSetRequests.insert(setId, qMakePair(access, 0)); @@ -2700,13 +2295,6 @@ void ApiWrap::resolveWebPages() { } } -void ApiWrap::requestParticipantsCountDelayed( - not_null<ChannelData*> channel) { - _participantsCountRequestTimer.call( - kReloadChannelMembersTimeout, - [=] { channel->updateFullForced(); }); -} - template <typename Request> void ApiWrap::requestFileReference( Data::FileOrigin origin, @@ -3220,88 +2808,6 @@ void ApiWrap::readFeaturedSets() { } } -void ApiWrap::parseChannelParticipants( - not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList, - Fn<void()> callbackNotModified) { - result.match([&](const MTPDchannels_channelParticipants &data) { - _session->data().processUsers(data.vusers()); - if (channel->mgInfo) { - refreshChannelAdmins(channel, data.vparticipants().v); - } - if (callbackList) { - callbackList(data.vcount().v, data.vparticipants().v); - } - }, [&](const MTPDchannels_channelParticipantsNotModified &) { - if (callbackNotModified) { - callbackNotModified(); - } else { - LOG(("API Error: " - "channels.channelParticipantsNotModified received!")); - } - }); -} - -void ApiWrap::refreshChannelAdmins( - not_null<ChannelData*> channel, - const QVector<MTPChannelParticipant> &participants) { - Data::ChannelAdminChanges changes(channel); - for (const auto &p : participants) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); - const auto userId = peerToUser(participantId); - p.match([&](const MTPDchannelParticipantAdmin &data) { - Assert(peerIsUser(participantId)); - changes.add(userId, qs(data.vrank().value_or_empty())); - }, [&](const MTPDchannelParticipantCreator &data) { - Assert(peerIsUser(participantId)); - const auto rank = qs(data.vrank().value_or_empty()); - if (const auto info = channel->mgInfo.get()) { - info->creator = channel->owner().userLoaded(userId); - info->creatorRank = rank; - } - changes.add(userId, rank); - }, [&](const auto &data) { - if (userId) { - changes.remove(userId); - } - }); - } -} - -void ApiWrap::parseRecentChannelParticipants( - not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList, - Fn<void()> callbackNotModified) { - parseChannelParticipants(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - auto applyLast = channel->isMegagroup() - && (channel->mgInfo->lastParticipants.size() <= list.size()); - if (applyLast) { - applyLastParticipantsList( - channel, - availableCount, - list); - } - if (callbackList) { - callbackList(availableCount, list); - } - }, std::move(callbackNotModified)); -} - void ApiWrap::jumpToDate(Dialogs::Key chat, const QDate &date) { if (const auto peer = chat.peer()) { jumpToHistoryDate(peer, date); @@ -3439,61 +2945,6 @@ void ApiWrap::checkForUnreadMentions( } } -void ApiWrap::addChatParticipants( - not_null<PeerData*> peer, - const std::vector<not_null<UserData*>> &users, - Fn<void(bool)> done) { - if (const auto chat = peer->asChat()) { - for (const auto &user : users) { - request(MTPmessages_AddChatUser( - chat->inputChat, - user->inputUser, - MTP_int(kForwardMessagesOnAdd) - )).done([=](const MTPUpdates &result) { - applyUpdates(result); - if (done) done(true); - }).fail([=](const MTP::Error &error) { - ShowAddParticipantsError(error.type(), peer, { 1, user }); - if (done) done(false); - }).afterDelay(crl::time(5)).send(); - } - } else if (const auto channel = peer->asChannel()) { - const auto hasBot = ranges::any_of(users, &UserData::isBot); - if (!peer->isMegagroup() && hasBot) { - ShowAddParticipantsError("USER_BOT", peer, users); - return; - } - auto list = QVector<MTPInputUser>(); - list.reserve(qMin(int(users.size()), int(kMaxUsersPerInvite))); - const auto send = [&] { - const auto callback = base::take(done); - request(MTPchannels_InviteToChannel( - channel->inputChannel, - MTP_vector<MTPInputUser>(list) - )).done([=](const MTPUpdates &result) { - applyUpdates(result); - requestParticipantsCountDelayed(channel); - if (callback) callback(true); - }).fail([=](const MTP::Error &error) { - ShowAddParticipantsError(error.type(), peer, users); - if (callback) callback(false); - }).afterDelay(crl::time(5)).send(); - }; - for (const auto &user : users) { - list.push_back(user->inputUser); - if (list.size() == kMaxUsersPerInvite) { - send(); - list.clear(); - } - } - if (!list.empty()) { - send(); - } - } else { - Unexpected("User in ApiWrap::addChatParticipants."); - } -} - void ApiWrap::requestSharedMediaCount( not_null<PeerData*> peer, Storage::SharedMediaType type) { @@ -4690,3 +4141,7 @@ Api::PeerPhoto &ApiWrap::peerPhoto() { Api::Polls &ApiWrap::polls() { return *_polls; } + +Api::ChatParticipants &ApiWrap::chatParticipants() { + return *_chatParticipants; +} diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index e9d60ff0a..80d109eff 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -66,6 +66,7 @@ class ViewsManager; class ConfirmPhone; class PeerPhoto; class Polls; +class ChatParticipants; namespace details { @@ -172,10 +173,6 @@ public: void requestPeer(not_null<PeerData*> peer); void requestPeers(const QList<PeerData*> &peers); void requestPeerSettings(not_null<PeerData*> peer); - void requestLastParticipants(not_null<ChannelData*> channel); - void requestBots(not_null<ChannelData*> channel); - void requestAdmins(not_null<ChannelData*> channel); - void requestParticipantsCountDelayed(not_null<ChannelData*> channel); using UpdatedFileReferences = Data::UpdatedFileReferences; using FileReferencesHandler = FnMut<void(const UpdatedFileReferences&)>; @@ -204,9 +201,6 @@ public: Fn<void(const MTP::Error &)> fail); void importChatInvite(const QString &hash, bool isGroup); - void requestChannelMembersForAdd( - not_null<ChannelData*> channel, - Fn<void(const MTPchannels_ChannelParticipants&)> callback); void processFullPeer( not_null<PeerData*> peer, const MTPmessages_ChatFull &result); @@ -219,17 +213,6 @@ public: void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items); void markMediaRead(not_null<HistoryItem*> item); - void requestSelfParticipant(not_null<ChannelData*> channel); - void kickParticipant( - not_null<ChatData*> chat, - not_null<PeerData*> participant); - void kickParticipant( - not_null<ChannelData*> channel, - not_null<PeerData*> participant, - ChatRestrictionsInfo currentRights); - void unblockParticipant( - not_null<ChannelData*> channel, - not_null<PeerData*> participant); void deleteAllFromParticipant( not_null<ChannelData*> channel, not_null<PeerData*> from); @@ -292,25 +275,6 @@ public: void readFeaturedSetDelayed(uint64 setId); - void parseChannelParticipants( - not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList, - Fn<void()> callbackNotModified = nullptr); - void parseRecentChannelParticipants( - not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList = nullptr, - Fn<void()> callbackNotModified = nullptr); - void addChatParticipants( - not_null<PeerData*> peer, - const std::vector<not_null<UserData*>> &users, - Fn<void(bool)> done = nullptr); - rpl::producer<SendAction> sendActions() const { return _sendActions.events(); } @@ -395,6 +359,7 @@ public: [[nodiscard]] Api::ConfirmPhone &confirmPhone(); [[nodiscard]] Api::PeerPhoto &peerPhoto(); [[nodiscard]] Api::Polls &polls(); + [[nodiscard]] Api::ChatParticipants &chatParticipants(); void updatePrivacyLastSeens(); @@ -458,14 +423,6 @@ private: not_null<UserData*> user, const MTPusers_UserFull &result, mtpRequestId req); - void applyLastParticipantsList( - not_null<ChannelData*> channel, - int availableCount, - const QVector<MTPChannelParticipant> &list); - void applyBotsList( - not_null<ChannelData*> channel, - int availableCount, - const QVector<MTPChannelParticipant> &list); void resolveWebPages(); void gotWebPages( ChannelData *channel, @@ -482,10 +439,6 @@ private: void requestSavedGifs(TimeId now); void readFeaturedSets(); - void refreshChannelAdmins( - not_null<ChannelData*> channel, - const QVector<MTPChannelParticipant> &participants); - void jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date); template <typename Callback> void requestMessageAfterDate( @@ -576,26 +529,10 @@ private: PeerRequests _peerRequests; base::flat_set<not_null<PeerData*>> _requestedPeerSettings; - PeerRequests _participantsRequests; - PeerRequests _botsRequests; - PeerRequests _adminsRequests; - base::DelayedCallTimer _participantsCountRequestTimer; - - ChannelData *_channelMembersForAdd = nullptr; - mtpRequestId _channelMembersForAddRequestId = 0; - Fn<void( - const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback; base::flat_map< not_null<History*>, std::pair<mtpRequestId,Fn<void()>>> _historyArchivedRequests; - using KickRequest = std::pair< - not_null<ChannelData*>, - not_null<PeerData*>>; - base::flat_map<KickRequest, mtpRequestId> _kickRequests; - - base::flat_set<not_null<ChannelData*>> _selfParticipantRequests; - QMap<WebPageData*, mtpRequestId> _webPagesPending; base::Timer _webPagesTimer; @@ -702,6 +639,7 @@ private: const std::unique_ptr<Api::ConfirmPhone> _confirmPhone; const std::unique_ptr<Api::PeerPhoto> _peerPhoto; const std::unique_ptr<Api::Polls> _polls; + const std::unique_ptr<Api::ChatParticipants> _chatParticipants; mtpRequestId _wallPaperRequestId = 0; QString _wallPaperSlug; diff --git a/Telegram/SourceFiles/boxes/delete_messages_box.cpp b/Telegram/SourceFiles/boxes/delete_messages_box.cpp index bcfd3ae27..30c33f7c6 100644 --- a/Telegram/SourceFiles/boxes/delete_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/delete_messages_box.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/delete_messages_box.h" #include "apiwrap.h" +#include "api/api_chat_participants.h" #include "base/unixtime.h" #include "data/data_channel.h" #include "data/data_chat.h" @@ -478,7 +479,7 @@ void DeleteMessagesBox::deleteAndClear() { } if (_moderateFrom) { if (_banUser && _banUser->checked()) { - _moderateInChannel->session().api().kickParticipant( + _moderateInChannel->session().api().chatParticipants().kick( _moderateInChannel, _moderateFrom, ChatRestrictionsInfo()); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 39aacbd06..2a189f214 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peer_list_controllers.h" +#include "api/api_chat_participants.h" #include "base/random.h" #include "ui/boxes/confirm_box.h" #include "ui/widgets/checkbox.h" @@ -71,7 +72,7 @@ void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat) { if (bot->isBot() && !bot->botInfo->startGroupToken.isEmpty()) { chat->session().api().sendBotStart(bot, chat); } else { - chat->session().api().addChatParticipants(chat, { 1, bot }); + chat->session().api().chatParticipants().add(chat, { 1, bot }); } Ui::hideLayer(); Ui::showPeerHistory(chat, ShowAtUnreadMsgId); diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index aa41ff240..e12ff3d1a 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peers/add_participants_box.h" +#include "api/api_chat_participants.h" #include "boxes/peers/edit_participant_box.h" #include "boxes/peers/edit_peer_type_box.h" #include "ui/boxes/confirm_box.h" @@ -231,7 +232,7 @@ bool AddParticipantsBoxController::inviteSelectedUsers( if (users.empty()) { return false; } - _peer->session().api().addChatParticipants(_peer, users); + _peer->session().api().chatParticipants().add(_peer, users); return true; } @@ -465,7 +466,7 @@ void AddSpecialBoxController::loadMoreRows() { )).done([=](const MTPchannels_ChannelParticipants &result) { _loadRequestId = 0; auto &session = channel->session(); - session.api().parseChannelParticipants(channel, result, [&]( + session.api().chatParticipants().parse(channel, result, [&]( int availableCount, const QVector<MTPChannelParticipant> &list) { for (const auto &data : list) { @@ -977,7 +978,7 @@ void AddSpecialBoxSearchController::searchParticipantsDone( _participantsQueries.erase(it); } }; - channel->session().api().parseChannelParticipants( + channel->session().api().chatParticipants().parse( channel, result, addToCache); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index dcdb409b1..59b1d0f26 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peers/edit_participants_box.h" +#include "api/api_chat_participants.h" #include "boxes/peer_list_controllers.h" #include "boxes/peers/edit_participant_box.h" #include "boxes/peers/add_participants_box.h" @@ -1396,12 +1397,12 @@ void ParticipantsBoxController::loadMoreRows() { && (_role == Role::Members || _role == Role::Profile); auto parseParticipants = [&](auto &&result, auto &&callback) { if (wasRecentRequest) { - channel->session().api().parseRecentChannelParticipants( + channel->session().api().chatParticipants().parseRecent( channel, result, callback); } else { - channel->session().api().parseChannelParticipants( + channel->session().api().chatParticipants().parse( channel, result, callback); @@ -1732,7 +1733,7 @@ void ParticipantsBoxController::unkickParticipant(not_null<UserData*> user) { delegate()->peerListRemoveRow(row); delegate()->peerListRefreshRows(); } - _peer->session().api().addChatParticipants(_peer, { 1, user }); + _peer->session().api().chatParticipants().add(_peer, { 1, user }); } void ParticipantsBoxController::kickParticipantSure( @@ -1750,9 +1751,12 @@ void ParticipantsBoxController::kickParticipantSure( } auto &session = _peer->session(); if (const auto chat = _peer->asChat()) { - session.api().kickParticipant(chat, participant); + session.api().chatParticipants().kick(chat, participant); } else if (const auto channel = _peer->asChannel()) { - session.api().kickParticipant(channel, participant, currentRights); + session.api().chatParticipants().kick( + channel, + participant, + currentRights); } } @@ -1803,7 +1807,9 @@ void ParticipantsBoxController::removeKickedWithRow( void ParticipantsBoxController::removeKicked( not_null<PeerData*> participant) { if (const auto channel = _peer->asChannel()) { - channel->session().api().unblockParticipant(channel, participant); + channel->session().api().chatParticipants().unblock( + channel, + participant); } } @@ -2010,7 +2016,7 @@ void ParticipantsBoxController::subscribeToCreatorChange( channel->mgInfo->lastAdmins.clear(); channel->mgInfo->lastRestricted.clear(); channel->mgInfo->lastParticipants.clear(); - api->parseRecentChannelParticipants(channel, result); + api->chatParticipants().parseRecent(channel, result); if (weak) { fullListRefresh(); } @@ -2175,7 +2181,7 @@ void ParticipantsBoxSearchController::searchDone( _queries.erase(it); } }; - _channel->session().api().parseChannelParticipants( + _channel->session().api().chatParticipants().parse( _channel, result, addToCache); diff --git a/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp b/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp index 74aecd992..d8ed2c237 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_invite_controller.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "calls/group/calls_group_invite_controller.h" +#include "api/api_chat_participants.h" #include "calls/group/calls_group_call.h" #include "calls/group/calls_group_menu.h" #include "boxes/peer_lists_box.h" @@ -221,7 +222,7 @@ object_ptr<Ui::BoxContent> PrepareInviteBox( const std::vector<not_null<UserData*>> &users, const std::vector<not_null<UserData*>> &nonMembers, Fn<void()> finish) { - peer->session().api().addChatParticipants( + peer->session().api().chatParticipants().add( peer, nonMembers, [=](bool) { invite(users); finish(); }); diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 870d0c6df..f0248ed7d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -49,7 +49,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "base/qt_signal_producer.h" #include "base/timer_rpl.h" -#include "apiwrap.h" // api().kickParticipant. +#include "apiwrap.h" // api().kick. +#include "api/api_chat_participants.h" // api().kick. #include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_media_devices.h" // UniqueDesktopCaptureSource. #include "webrtc/webrtc_audio_input_tester.h" @@ -1344,7 +1345,7 @@ void Panel::showBox( void Panel::kickParticipantSure(not_null<PeerData*> participantPeer) { if (const auto chat = _peer->asChat()) { - chat->session().api().kickParticipant(chat, participantPeer); + chat->session().api().chatParticipants().kick(chat, participantPeer); } else if (const auto channel = _peer->asChannel()) { const auto currentRestrictedRights = [&] { const auto user = participantPeer->asUser(); @@ -1356,7 +1357,7 @@ void Panel::kickParticipantSure(not_null<PeerData*> participantPeer) { ? i->second.rights : ChatRestrictionsInfo(); }(); - channel->session().api().kickParticipant( + channel->session().api().chatParticipants().kick( channel, participantPeer, currentRestrictedRights); diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 48ac4888a..39d2a214d 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/message_field.h" // PrepareMentionTag. #include "mainwindow.h" #include "apiwrap.h" +#include "api/api_chat_participants.h" #include "main/main_session.h" #include "storage/storage_account.h" #include "core/application.h" @@ -431,7 +432,8 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { } } else if (_channel && _channel->isMegagroup()) { if (_channel->lastParticipantsRequestNeeded()) { - _channel->session().api().requestLastParticipants(_channel); + _channel->session().api().chatParticipants().requestLast( + _channel); } else { mrows.reserve(mrows.size() + _channel->mgInfo->lastParticipants.size()); for (const auto user : _channel->mgInfo->lastParticipants) { @@ -489,7 +491,8 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { } else if (_channel && _channel->isMegagroup()) { if (_channel->mgInfo->bots.empty()) { if (!_channel->mgInfo->botStatus) { - _channel->session().api().requestBots(_channel); + _channel->session().api().chatParticipants().requestBots( + _channel); } } else { const auto &commands = _channel->mgInfo->botCommands(); diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 1d9b4fce9..4944ebba6 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -927,77 +927,4 @@ void ApplyChannelUpdate( channel->owner().sendHistoryChangeNotifications(); } -void ApplyMegagroupAdmins( - not_null<ChannelData*> channel, - const MTPDchannels_channelParticipants &data) { - Expects(channel->isMegagroup()); - - channel->owner().processUsers(data.vusers()); - - const auto &list = data.vparticipants().v; - const auto i = ranges::find( - list, - mtpc_channelParticipantCreator, - &MTPChannelParticipant::type); - if (i != list.end()) { - const auto &data = i->c_channelParticipantCreator(); - const auto userId = data.vuser_id().v; - channel->mgInfo->creator = channel->owner().userLoaded(userId); - channel->mgInfo->creatorRank = qs(data.vrank().value_or_empty()); - } else { - channel->mgInfo->creator = nullptr; - channel->mgInfo->creatorRank = QString(); - } - - auto adding = base::flat_map<UserId, QString>(); - auto admins = ranges::make_subrange( - list.begin(), list.end() - ) | ranges::views::transform([](const MTPChannelParticipant &p) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); - const auto rank = p.match([](const MTPDchannelParticipantAdmin &data) { - return qs(data.vrank().value_or_empty()); - }, [](const MTPDchannelParticipantCreator &data) { - return qs(data.vrank().value_or_empty()); - }, [](const auto &data) { - return QString(); - }); - return std::make_pair(participantId, rank); - }) | ranges::views::filter([](const auto &pair) { - return peerIsUser(pair.first); - }); - for (const auto &[participantId, rank] : admins) { - Assert(peerIsUser(participantId)); - adding.emplace(peerToUser(participantId), rank); - } - if (channel->mgInfo->creator) { - adding.emplace( - peerToUser(channel->mgInfo->creator->id), - channel->mgInfo->creatorRank); - } - auto removing = channel->mgInfo->admins; - if (removing.empty() && adding.empty()) { - // Add some admin-placeholder so we don't DDOS - // server with admins list requests. - LOG(("API Error: Got empty admins list from server.")); - adding.emplace(0, QString()); - } - - Data::ChannelAdminChanges changes(channel); - for (const auto &[addingId, rank] : adding) { - if (!removing.remove(addingId)) { - changes.add(addingId, rank); - } - } - for (const auto &[removingId, rank] : removing) { - changes.remove(removingId); - } -} - } // namespace Data diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index ec5a0ff57..e1444813d 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -481,8 +481,4 @@ void ApplyChannelUpdate( not_null<ChannelData*> channel, const MTPDchannelFull &update); -void ApplyMegagroupAdmins( - not_null<ChannelData*> channel, - const MTPDchannels_channelParticipants &data); - } // namespace Data diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 112b42636..a99105659 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/crc32hash.h" #include "lang/lang_keys.h" #include "apiwrap.h" +#include "api/api_chat_participants.h" #include "ui/boxes/confirm_box.h" #include "main/main_session.h" #include "main/main_session_settings.h" @@ -644,7 +645,7 @@ void PeerData::updateFullForced() { session().api().requestFullPeer(this); if (const auto channel = asChannel()) { if (!channel->amCreator() && !channel->inviter) { - session().api().requestSelfParticipant(channel); + session().api().chatParticipants().requestSelf(channel); } } } diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index c8ea855df..2e932c38a 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwidget.h" #include "core/application.h" #include "apiwrap.h" +#include "api/api_chat_participants.h" #include "api/api_attached_stickers.h" #include "window/window_session_controller.h" #include "main/main_session.h" @@ -444,7 +445,7 @@ void InnerWidget::requestAdmins() { MTP_int(kMaxChannelAdmins), MTP_long(participantsHash) )).done([=](const MTPchannels_ChannelParticipants &result) { - session().api().parseChannelParticipants(_channel, result, [&]( + session().api().chatParticipants().parse(_channel, result, [&]( int availableCount, const QVector<MTPChannelParticipant> &list) { auto filtered = ( diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index b1fffd857..81fbd49de 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_histories.h" #include "lang/lang_keys.h" #include "apiwrap.h" +#include "api/api_chat_participants.h" #include "mainwidget.h" #include "mainwindow.h" #include "main/main_session.h" @@ -2587,7 +2588,7 @@ void History::applyDialog( data.vtop_message().v); if (const auto item = owner().message(topMessageId)) { if (item->date() <= channel->date) { - session().api().requestSelfParticipant(channel); + session().api().chatParticipants().requestSelf(channel); } } } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index fff5b2c9b..8f437ef34 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_editing.h" #include "api/api_bot.h" +#include "api/api_chat_participants.h" #include "api/api_sending.h" #include "api/api_text_entities.h" #include "api/api_send_progress.h" @@ -5675,7 +5676,8 @@ void HistoryWidget::handlePeerMigration() { showHistory( channel->id, (_showAtMsgId > 0) ? (-_showAtMsgId) : _showAtMsgId); - channel->session().api().requestParticipantsCountDelayed(channel); + channel->session().api().chatParticipants().requestCountDelayed( + channel); } else { _migrated = _history->migrateFrom(); _list->notifyMigrateUpdated(); @@ -6737,10 +6739,10 @@ void HistoryWidget::handlePeerUpdate() { session().api().requestFullPeer(_peer); } else if (auto channel = _peer->asMegagroup()) { if (!channel->mgInfo->botStatus) { - session().api().requestBots(channel); + session().api().chatParticipants().requestBots(channel); } if (channel->mgInfo->admins.empty()) { - session().api().requestAdmins(channel); + session().api().chatParticipants().requestAdmins(channel); } } if (!_a_show.animating()) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index f4671c7e4..735b4dcc7 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_web_page.h" #include "storage/storage_account.h" #include "apiwrap.h" +#include "api/api_chat_participants.h" #include "ui/boxes/confirm_box.h" #include "history/history.h" #include "history/history_item.h" @@ -672,7 +673,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) { session().api().requestFullPeer(peer); } else if (const auto channel = peer->asMegagroup()) { if (!channel->mgInfo->botStatus) { - session().api().requestBots(channel); + session().api().chatParticipants().requestBots(channel); } } else if (hasSilentBroadcastToggle()) { _silent = std::make_unique<Ui::SilentToggle>( diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index ea30062bb..bb3682e14 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -51,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "support/support_helper.h" #include "apiwrap.h" +#include "api/api_chat_participants.h" #include "styles/style_window.h" #include "styles/style_dialogs.h" #include "styles/style_chat.h" @@ -1143,7 +1144,7 @@ void TopBarWidget::updateOnlineDisplay() { && (channel->membersCount() <= channel->session().serverConfig().chatSizeMax)) { if (channel->lastParticipantsRequestNeeded()) { - session().api().requestLastParticipants(channel); + session().api().chatParticipants().requestLast(channel); } const auto self = session().user(); auto online = 0; diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp index 16e22304a..707143fd2 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "profile/profile_block_group_members.h" +#include "api/api_chat_participants.h" #include "styles/style_profile.h" #include "ui/widgets/labels.h" #include "ui/boxes/confirm_box.h" @@ -89,10 +90,10 @@ void GroupMembersWidget::removePeer(PeerData *selectedPeer) { const auto callback = [=] { Ui::hideLayer(); if (const auto chat = peer->asChat()) { - chat->session().api().kickParticipant(chat, user); + chat->session().api().chatParticipants().kick(chat, user); Ui::showPeerHistory(chat, ShowAtTheEndMsgId); } else if (const auto channel = peer->asChannel()) { - channel->session().api().kickParticipant( + channel->session().api().chatParticipants().kick( channel, user, currentRestrictedRights); @@ -158,7 +159,7 @@ void GroupMembersWidget::preloadMore() { //if (auto megagroup = peer()->asMegagroup()) { // auto &megagroupInfo = megagroup->mgInfo; // if (!megagroupInfo->lastParticipants.isEmpty() && megagroupInfo->lastParticipants.size() < megagroup->membersCount()) { - // peer()->session().api().requestLastParticipants(megagroup, false); + // peer()->session().api().requestLast(megagroup, false); // } //} } @@ -198,7 +199,8 @@ void GroupMembersWidget::refreshMembers() { fillChatMembers(chat); } else if (const auto megagroup = peer()->asMegagroup()) { if (megagroup->lastParticipantsRequestNeeded()) { - megagroup->session().api().requestLastParticipants(megagroup); + megagroup->session().api().chatParticipants().requestLast( + megagroup); } fillMegagroupMembers(megagroup); } diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 2abdd59cf..d70773c30 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/window_peer_menu.h" +#include "api/api_chat_participants.h" #include "lang/lang_keys.h" #include "ui/boxes/confirm_box.h" #include "boxes/delete_messages_box.h" @@ -1090,9 +1091,9 @@ void PeerMenuAddChannelMembers( return; } const auto api = &channel->session().api(); - api->requestChannelMembersForAdd(channel, crl::guard(navigation, [=]( + api->chatParticipants().requestForAdd(channel, crl::guard(navigation, [=]( const MTPchannels_ChannelParticipants &result) { - api->parseChannelParticipants(channel, result, [&]( + api->chatParticipants().parse(channel, result, [&]( int availableCount, const QVector<MTPChannelParticipant> &list) { auto already = ( From 503814ad4e19131f788c6b377c96ec2a224af803 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 24 Nov 2021 07:25:05 +0300 Subject: [PATCH 090/180] Slightly refactored Api::ChatParticipants. --- .../SourceFiles/api/api_chat_participants.cpp | 543 ++++++++---------- .../SourceFiles/api/api_chat_participants.h | 45 +- .../boxes/peers/edit_participants_box.cpp | 11 +- 3 files changed, 268 insertions(+), 331 deletions(-) diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index 5a6928513..15eda68ee 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -35,6 +35,16 @@ constexpr auto kMaxUsersPerInvite = 100; // that was added to this chat. constexpr auto kForwardMessagesOnAdd = 100; +[[nodiscard]] PeerId TLToPeerId(const MTPChannelParticipant &p) { + return p.match([](const MTPDchannelParticipantBanned &data) { + return peerFromMTP(data.vpeer()); + }, [](const MTPDchannelParticipantLeft &data) { + return peerFromMTP(data.vpeer()); + }, [](const auto &data) { + return peerFromUser(data.vuser_id()); + }); +} + void ApplyMegagroupAdmins( not_null<ChannelData*> channel, const MTPDchannels_channelParticipants &data) { @@ -61,14 +71,7 @@ void ApplyMegagroupAdmins( auto admins = ranges::make_subrange( list.begin(), list.end() ) | ranges::views::transform([](const MTPChannelParticipant &p) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); + const auto participantId = TLToPeerId(p); const auto rank = p.match([](const MTPDchannelParticipantAdmin &data) { return qs(data.vrank().value_or_empty()); }, [](const MTPDchannelParticipantCreator &data) { @@ -108,56 +111,12 @@ void ApplyMegagroupAdmins( } } -} // namespace - -ChatParticipants::ChatParticipants(not_null<ApiWrap*> api) -: _session(&api->session()) -, _api(&api->instance()) { -} - -void ChatParticipants::requestForAdd( +void RefreshChannelAdmins( not_null<ChannelData*> channel, - Fn<void(const MTPchannels_ChannelParticipants&)> callback) { - _channelMembersForAddCallback = std::move(callback); - if (_channelMembersForAdd == channel) { - return; - } - _api.request(base::take(_channelMembersForAddRequestId)).cancel(); - - const auto offset = 0; - const auto participantsHash = uint64(0); - - _channelMembersForAdd = channel; - _channelMembersForAddRequestId = _api.request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsRecent(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - base::take(_channelMembersForAddRequestId); - base::take(_channelMembersForAdd); - base::take(_channelMembersForAddCallback)(result); - }).fail([=](const MTP::Error &error) { - base::take(_channelMembersForAddRequestId); - base::take(_channelMembersForAdd); - base::take(_channelMembersForAddCallback); - }).send(); -} - -void ChatParticipants::refreshChannelAdmins( - not_null<ChannelData*> channel, - const QVector<MTPChannelParticipant> &participants) { + ChatParticipants::TLMembersList participants) { Data::ChannelAdminChanges changes(channel); for (const auto &p : participants) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); + const auto participantId = TLToPeerId(p); const auto userId = peerToUser(participantId); p.match([&](const MTPDchannelParticipantAdmin &data) { Assert(peerIsUser(participantId)); @@ -178,229 +137,24 @@ void ChatParticipants::refreshChannelAdmins( } } -void ChatParticipants::requestLast(not_null<ChannelData*> channel) { - if (!channel->isMegagroup() - || _participantsRequests.contains(channel)) { - return; - } - - const auto offset = 0; - const auto participantsHash = uint64(0); - const auto requestId = _api.request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsRecent(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - _participantsRequests.remove(channel); - parse(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - applyLastList( - channel, - availableCount, - list); - }); - }).fail([this, channel](const MTP::Error &error) { - _participantsRequests.remove(channel); - }).send(); - - _participantsRequests[channel] = requestId; -} - -void ChatParticipants::requestBots(not_null<ChannelData*> channel) { - if (!channel->isMegagroup() || _botsRequests.contains(channel)) { - return; - } - - const auto offset = 0; - const auto participantsHash = uint64(0); - const auto requestId = _api.request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsBots(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - _botsRequests.remove(channel); - parse(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - applyBotsList( - channel, - availableCount, - list); - }); - }).fail([=](const MTP::Error &error) { - _botsRequests.remove(channel); - }).send(); - - _botsRequests[channel] = requestId; -} - -void ChatParticipants::requestAdmins(not_null<ChannelData*> channel) { - if (!channel->isMegagroup() || _adminsRequests.contains(channel)) { - return; - } - - const auto offset = 0; - const auto participantsHash = uint64(0); - const auto requestId = _api.request(MTPchannels_GetParticipants( - channel->inputChannel, - MTP_channelParticipantsAdmins(), - MTP_int(offset), - MTP_int(_session->serverConfig().chatSizeMax), - MTP_long(participantsHash) - )).done([=](const MTPchannels_ChannelParticipants &result) { - _adminsRequests.remove(channel); - result.match([&](const MTPDchannels_channelParticipants &data) { - ApplyMegagroupAdmins(channel, data); - }, [&](const MTPDchannels_channelParticipantsNotModified &) { - LOG(("API Error: channels.channelParticipantsNotModified received!")); - }); - }).fail([=](const MTP::Error &error) { - _adminsRequests.remove(channel); - }).send(); - - _adminsRequests[channel] = requestId; -} - -void ChatParticipants::requestCountDelayed( - not_null<ChannelData*> channel) { - _participantsCountRequestTimer.call( - kReloadChannelMembersTimeout, - [=] { channel->updateFullForced(); }); -} - -void ChatParticipants::add( - not_null<PeerData*> peer, - const std::vector<not_null<UserData*>> &users, - Fn<void(bool)> done) { - if (const auto chat = peer->asChat()) { - for (const auto &user : users) { - _api.request(MTPmessages_AddChatUser( - chat->inputChat, - user->inputUser, - MTP_int(kForwardMessagesOnAdd) - )).done([=](const MTPUpdates &result) { - _session->api().applyUpdates(result); - if (done) done(true); - }).fail([=](const MTP::Error &error) { - ShowAddParticipantsError(error.type(), peer, { 1, user }); - if (done) done(false); - }).afterDelay(kSmallDelayMs).send(); - } - } else if (const auto channel = peer->asChannel()) { - const auto hasBot = ranges::any_of(users, &UserData::isBot); - if (!peer->isMegagroup() && hasBot) { - ShowAddParticipantsError("USER_BOT", peer, users); - return; - } - auto list = QVector<MTPInputUser>(); - list.reserve(std::min(int(users.size()), int(kMaxUsersPerInvite))); - const auto send = [&] { - const auto callback = base::take(done); - _api.request(MTPchannels_InviteToChannel( - channel->inputChannel, - MTP_vector<MTPInputUser>(list) - )).done([=](const MTPUpdates &result) { - _session->api().applyUpdates(result); - requestCountDelayed(channel); - if (callback) callback(true); - }).fail([=](const MTP::Error &error) { - ShowAddParticipantsError(error.type(), peer, users); - if (callback) callback(false); - }).afterDelay(kSmallDelayMs).send(); - }; - for (const auto &user : users) { - list.push_back(user->inputUser); - if (list.size() == kMaxUsersPerInvite) { - send(); - list.clear(); - } - } - if (!list.empty()) { - send(); - } - } else { - Unexpected("User in ChatParticipants::add."); - } -} - -void ChatParticipants::parseRecent( - not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList, - Fn<void()> callbackNotModified) { - parse(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - auto applyLast = channel->isMegagroup() - && (channel->mgInfo->lastParticipants.size() <= list.size()); - if (applyLast) { - applyLastList( - channel, - availableCount, - list); - } - if (callbackList) { - callbackList(availableCount, list); - } - }, std::move(callbackNotModified)); -} - -void ChatParticipants::parse( - not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList, - Fn<void()> callbackNotModified) { - result.match([&](const MTPDchannels_channelParticipants &data) { - _session->data().processUsers(data.vusers()); - if (channel->mgInfo) { - refreshChannelAdmins(channel, data.vparticipants().v); - } - if (callbackList) { - callbackList(data.vcount().v, data.vparticipants().v); - } - }, [&](const MTPDchannels_channelParticipantsNotModified &) { - if (callbackNotModified) { - callbackNotModified(); - } else { - LOG(("API Error: " - "channels.channelParticipantsNotModified received!")); - } - }); -} - -void ChatParticipants::applyLastList( +void ApplyLastList( not_null<ChannelData*> channel, int availableCount, - const QVector<MTPChannelParticipant> &list) { + ChatParticipants::TLMembersList list) { channel->mgInfo->lastAdmins.clear(); channel->mgInfo->lastRestricted.clear(); channel->mgInfo->lastParticipants.clear(); - channel->mgInfo->lastParticipantsStatus = MegagroupInfo::LastParticipantsUpToDate - | MegagroupInfo::LastParticipantsOnceReceived; + channel->mgInfo->lastParticipantsStatus = + MegagroupInfo::LastParticipantsUpToDate + | MegagroupInfo::LastParticipantsOnceReceived; auto botStatus = channel->mgInfo->botStatus; for (const auto &p : list) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); + const auto participantId = TLToPeerId(p); if (!participantId) { continue; } - const auto participant = _session->data().peer(participantId); + const auto participant = channel->owner().peer(participantId); const auto user = participant->asUser(); const auto adminCanEdit = (p.type() == mtpc_channelParticipantAdmin) ? p.c_channelParticipantAdmin().is_can_edit() @@ -460,21 +214,21 @@ void ChatParticipants::applyLastList( //} else { // channel->setMembersCount(availableCount); //} - _session->changes().peerUpdated( + channel->session().changes().peerUpdated( channel, (Data::PeerUpdate::Flag::Members | Data::PeerUpdate::Flag::Admins)); channel->mgInfo->botStatus = botStatus; - _session->changes().peerUpdated( + channel->session().changes().peerUpdated( channel, Data::PeerUpdate::Flag::FullInfo); } -void ChatParticipants::applyBotsList( +void ApplyBotsList( not_null<ChannelData*> channel, int availableCount, - const QVector<MTPChannelParticipant> &list) { - const auto history = _session->data().historyLoaded(channel); + ChatParticipants::TLMembersList list) { + const auto history = channel->owner().historyLoaded(channel); channel->mgInfo->bots.clear(); channel->mgInfo->botStatus = -1; @@ -482,19 +236,12 @@ void ChatParticipants::applyBotsList( auto botStatus = channel->mgInfo->botStatus; auto keyboardBotFound = !history || !history->lastKeyboardFrom; for (const auto &p : list) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); + const auto participantId = TLToPeerId(p); if (!participantId) { continue; } - const auto participant = _session->data().peer(participantId); + const auto participant = channel->owner().peer(participantId); const auto user = participant->asUser(); if (user && user->isBot()) { channel->mgInfo->bots.insert(user); @@ -509,18 +256,234 @@ void ChatParticipants::applyBotsList( } } if (needBotsInfos) { - _session->api().requestFullPeer(channel); + channel->session().api().requestFullPeer(channel); } if (!keyboardBotFound) { history->clearLastKeyboard(); } channel->mgInfo->botStatus = botStatus; - _session->changes().peerUpdated( + channel->session().changes().peerUpdated( channel, Data::PeerUpdate::Flag::FullInfo); } +} // namespace + +ChatParticipants::ChatParticipants(not_null<ApiWrap*> api) +: _api(&api->instance()) { +} + +void ChatParticipants::requestForAdd( + not_null<ChannelData*> channel, + Fn<void(const TLMembers&)> callback) { + Expects(callback != nullptr); + _forAdd.callback = std::move(callback); + if (_forAdd.channel == channel) { + return; + } + _api.request(base::take(_forAdd.requestId)).cancel(); + + const auto offset = 0; + const auto participantsHash = uint64(0); + + _forAdd.channel = channel; + _forAdd.requestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsRecent(), + MTP_int(offset), + MTP_int(channel->session().serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + base::take(_forAdd).callback(result); + }).fail([=](const MTP::Error &error) { + base::take(_forAdd); + }).send(); +} + +void ChatParticipants::requestLast(not_null<ChannelData*> channel) { + if (!channel->isMegagroup() + || _participantsRequests.contains(channel)) { + return; + } + + const auto offset = 0; + const auto participantsHash = uint64(0); + const auto requestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsRecent(), + MTP_int(offset), + MTP_int(channel->session().serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + _participantsRequests.remove(channel); + parse(channel, result, [&](int availableCount, TLMembersList list) { + ApplyLastList( + channel, + availableCount, + list); + }); + }).fail([this, channel](const MTP::Error &error) { + _participantsRequests.remove(channel); + }).send(); + + _participantsRequests[channel] = requestId; +} + +void ChatParticipants::requestBots(not_null<ChannelData*> channel) { + if (!channel->isMegagroup() || _botsRequests.contains(channel)) { + return; + } + + const auto offset = 0; + const auto participantsHash = uint64(0); + const auto requestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsBots(), + MTP_int(offset), + MTP_int(channel->session().serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + _botsRequests.remove(channel); + parse(channel, result, [&](int availableCount, TLMembersList list) { + ApplyBotsList( + channel, + availableCount, + list); + }); + }).fail([=](const MTP::Error &error) { + _botsRequests.remove(channel); + }).send(); + + _botsRequests[channel] = requestId; +} + +void ChatParticipants::requestAdmins(not_null<ChannelData*> channel) { + if (!channel->isMegagroup() || _adminsRequests.contains(channel)) { + return; + } + + const auto offset = 0; + const auto participantsHash = uint64(0); + const auto requestId = _api.request(MTPchannels_GetParticipants( + channel->inputChannel, + MTP_channelParticipantsAdmins(), + MTP_int(offset), + MTP_int(channel->session().serverConfig().chatSizeMax), + MTP_long(participantsHash) + )).done([=](const MTPchannels_ChannelParticipants &result) { + _adminsRequests.remove(channel); + result.match([&](const MTPDchannels_channelParticipants &data) { + ApplyMegagroupAdmins(channel, data); + }, [](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); + }); + }).fail([=](const MTP::Error &error) { + _adminsRequests.remove(channel); + }).send(); + + _adminsRequests[channel] = requestId; +} + +void ChatParticipants::requestCountDelayed( + not_null<ChannelData*> channel) { + _participantsCountRequestTimer.call( + kReloadChannelMembersTimeout, + [=] { channel->updateFullForced(); }); +} + +void ChatParticipants::add( + not_null<PeerData*> peer, + const std::vector<not_null<UserData*>> &users, + Fn<void(bool)> done) { + if (const auto chat = peer->asChat()) { + for (const auto &user : users) { + _api.request(MTPmessages_AddChatUser( + chat->inputChat, + user->inputUser, + MTP_int(kForwardMessagesOnAdd) + )).done([=](const MTPUpdates &result) { + chat->session().api().applyUpdates(result); + if (done) done(true); + }).fail([=](const MTP::Error &error) { + ShowAddParticipantsError(error.type(), peer, { 1, user }); + if (done) done(false); + }).afterDelay(kSmallDelayMs).send(); + } + } else if (const auto channel = peer->asChannel()) { + const auto hasBot = ranges::any_of(users, &UserData::isBot); + if (!peer->isMegagroup() && hasBot) { + ShowAddParticipantsError("USER_BOT", peer, users); + return; + } + auto list = QVector<MTPInputUser>(); + list.reserve(std::min(int(users.size()), int(kMaxUsersPerInvite))); + const auto send = [&] { + const auto callback = base::take(done); + _api.request(MTPchannels_InviteToChannel( + channel->inputChannel, + MTP_vector<MTPInputUser>(list) + )).done([=](const MTPUpdates &result) { + channel->session().api().applyUpdates(result); + requestCountDelayed(channel); + if (callback) callback(true); + }).fail([=](const MTP::Error &error) { + ShowAddParticipantsError(error.type(), peer, users); + if (callback) callback(false); + }).afterDelay(kSmallDelayMs).send(); + }; + for (const auto &user : users) { + list.push_back(user->inputUser); + if (list.size() == kMaxUsersPerInvite) { + send(); + list.clear(); + } + } + if (!list.empty()) { + send(); + } + } else { + Unexpected("User in ChatParticipants::add."); + } +} + +void ChatParticipants::parseRecent( + not_null<ChannelData*> channel, + const TLMembers &result, + Fn<void(int availableCount, TLMembersList list)> callbackList) { + parse(channel, result, [&](int availableCount, TLMembersList list) { + const auto applyLast = channel->isMegagroup() + && (channel->mgInfo->lastParticipants.size() <= list.size()); + if (applyLast) { + ApplyLastList( + channel, + availableCount, + list); + } + if (callbackList) { + callbackList(availableCount, list); + } + }); +} + +void ChatParticipants::parse( + not_null<ChannelData*> channel, + const TLMembers &result, + Fn<void(int availableCount, TLMembersList list)> callbackList) { + result.match([&](const MTPDchannels_channelParticipants &data) { + channel->owner().processUsers(data.vusers()); + if (channel->mgInfo) { + RefreshChannelAdmins(channel, data.vparticipants().v); + } + if (callbackList) { + callbackList(data.vcount().v, data.vparticipants().v); + } + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: channels.channelParticipantsNotModified received!")); + }); +} + void ChatParticipants::requestSelf(not_null<ChannelData*> channel) { if (_selfParticipantRequests.contains(channel)) { return; @@ -533,7 +496,7 @@ void ChatParticipants::requestSelf(not_null<ChannelData*> channel) { channel->inviter = inviter; channel->inviteDate = inviteDate; channel->inviteViaRequest = inviteViaRequest; - if (const auto history = _session->data().historyLoaded(channel)) { + if (const auto history = channel->owner().historyLoaded(channel)) { if (history->lastMessageKnown()) { history->checkLocalMessages(); history->owner().sendHistoryChangeNotifications(); @@ -549,7 +512,7 @@ void ChatParticipants::requestSelf(not_null<ChannelData*> channel) { )).done([=](const MTPchannels_ChannelParticipant &result) { _selfParticipantRequests.erase(channel); result.match([&](const MTPDchannels_channelParticipant &data) { - _session->data().processUsers(data.vusers()); + channel->owner().processUsers(data.vusers()); const auto &participant = data.vparticipant(); participant.match([&](const MTPDchannelParticipantSelf &data) { @@ -559,9 +522,9 @@ void ChatParticipants::requestSelf(not_null<ChannelData*> channel) { data.is_via_request()); }, [&](const MTPDchannelParticipantCreator &) { if (channel->mgInfo) { - channel->mgInfo->creator = _session->user(); + channel->mgInfo->creator = channel->session().user(); } - finalize(_session->userId(), channel->date); + finalize(channel->session().userId(), channel->date); }, [&](const MTPDchannelParticipantAdmin &data) { const auto inviter = data.is_self() ? data.vinviter_id().value_or(-1) @@ -597,7 +560,7 @@ void ChatParticipants::kick( chat->inputChat, participant->asUser()->inputUser )).done([=](const MTPUpdates &result) { - _session->api().applyUpdates(result); + chat->session().api().applyUpdates(result); }).send(); } @@ -617,7 +580,7 @@ void ChatParticipants::kick( MTPDchatBannedRights::Flags::from_raw(uint32(rights.flags))), MTP_int(rights.until)) )).done([=](const MTPUpdates &result) { - _session->api().applyUpdates(result); + channel->session().api().applyUpdates(result); _kickRequests.remove(KickRequest(channel, participant)); channel->applyEditBanned(participant, currentRights, rights); @@ -641,7 +604,7 @@ void ChatParticipants::unblock( participant->input, MTP_chatBannedRights(MTP_flags(0), MTP_int(0)) )).done([=](const MTPUpdates &result) { - _session->api().applyUpdates(result); + channel->session().api().applyUpdates(result); _kickRequests.remove(KickRequest(channel, participant)); if (channel->kickedCount() > 0) { diff --git a/Telegram/SourceFiles/api/api_chat_participants.h b/Telegram/SourceFiles/api/api_chat_participants.h index 6794c4db2..be07fad94 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.h +++ b/Telegram/SourceFiles/api/api_chat_participants.h @@ -15,14 +15,12 @@ class ChannelData; struct ChatRestrictionsInfo; -namespace Main { -class Session; -} // namespace Main - namespace Api { class ChatParticipants final { public: + using TLMembers = MTPchannels_ChannelParticipants; + using TLMembersList = const QVector<MTPChannelParticipant> &; explicit ChatParticipants(not_null<ApiWrap*> api); void requestLast(not_null<ChannelData*> channel); @@ -32,18 +30,12 @@ public: void parse( not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList, - Fn<void()> callbackNotModified = nullptr); + const TLMembers &result, + Fn<void(int availableCount, TLMembersList list)> callbackList); void parseRecent( not_null<ChannelData*> channel, - const MTPchannels_ChannelParticipants &result, - Fn<void( - int availableCount, - const QVector<MTPChannelParticipant> &list)> callbackList = nullptr, - Fn<void()> callbackNotModified = nullptr); + const TLMembers &result, + Fn<void(int availableCount, TLMembersList)> callbackList = nullptr); void add( not_null<PeerData*> peer, const std::vector<not_null<UserData*>> &users, @@ -53,7 +45,7 @@ public: void requestForAdd( not_null<ChannelData*> channel, - Fn<void(const MTPchannels_ChannelParticipants&)> callback); + Fn<void(const TLMembers&)> callback); void kick( not_null<ChatData*> chat, @@ -67,20 +59,6 @@ public: not_null<PeerData*> participant); private: - void applyLastList( - not_null<ChannelData*> channel, - int availableCount, - const QVector<MTPChannelParticipant> &list); - void applyBotsList( - not_null<ChannelData*> channel, - int availableCount, - const QVector<MTPChannelParticipant> &list); - - void refreshChannelAdmins( - not_null<ChannelData*> channel, - const QVector<MTPChannelParticipant> &participants); - - const not_null<Main::Session*> _session; MTP::Sender _api; using PeerRequests = base::flat_map<PeerData*, mtpRequestId>; @@ -90,10 +68,11 @@ private: PeerRequests _adminsRequests; base::DelayedCallTimer _participantsCountRequestTimer; - ChannelData *_channelMembersForAdd = nullptr; - mtpRequestId _channelMembersForAddRequestId = 0; - Fn<void( - const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback; + struct { + ChannelData *channel = nullptr; + mtpRequestId requestId = 0; + Fn<void(const TLMembers&)> callback; + } _forAdd; base::flat_set<not_null<ChannelData*>> _selfParticipantRequests; diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 59b1d0f26..92bf2a79a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -1396,16 +1396,11 @@ void ParticipantsBoxController::loadMoreRows() { auto wasRecentRequest = firstLoad && (_role == Role::Members || _role == Role::Profile); auto parseParticipants = [&](auto &&result, auto &&callback) { + auto &api = channel->session().api().chatParticipants(); if (wasRecentRequest) { - channel->session().api().chatParticipants().parseRecent( - channel, - result, - callback); + api.parseRecent(channel, result, callback); } else { - channel->session().api().chatParticipants().parse( - channel, - result, - callback); + api.parse(channel, result, callback); } }; parseParticipants(result, [&]( From c65468f270bb7e73f615038a521cf079aa15ceec Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 24 Nov 2021 07:25:05 +0300 Subject: [PATCH 091/180] Added new class Api::ChatParticipant. --- .../SourceFiles/api/api_chat_participants.cpp | 114 ++++++++++++++++++ .../SourceFiles/api/api_chat_participants.h | 57 ++++++++- 2 files changed, 169 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index 15eda68ee..d6d4404c2 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -270,6 +270,120 @@ void ApplyBotsList( } // namespace +ChatParticipant::ChatParticipant( + const MTPChannelParticipant &p, + not_null<PeerData*> peer) { + _peer = p.match([](const MTPDchannelParticipantBanned &data) { + return peerFromMTP(data.vpeer()); + }, [](const MTPDchannelParticipantLeft &data) { + return peerFromMTP(data.vpeer()); + }, [](const auto &data) { + return peerFromUser(data.vuser_id()); + }); + + p.match([&](const MTPDchannelParticipantCreator &data) { + _canBeEdited = (peer->session().userPeerId() == _peer); + _type = Type::Creator; + _rights = ChatAdminRightsInfo(data.vadmin_rights()); + _rank = qs(data.vrank().value_or_empty()); + }, [&](const MTPDchannelParticipantAdmin &data) { + _canBeEdited = data.is_can_edit(); + _type = Type::Admin; + _rank = qs(data.vrank().value_or_empty()); + _rights = ChatAdminRightsInfo(data.vadmin_rights()); + _by = peerToUser(peerFromUser(data.vpromoted_by())); + }, [&](const MTPDchannelParticipantSelf &data) { + _type = Type::Member; + _by = peerToUser(peerFromUser(data.vinviter_id())); + }, [&](const MTPDchannelParticipant &data) { + _type = Type::Member; + }, [&](const MTPDchannelParticipantBanned &data) { + _restrictions = ChatRestrictionsInfo(data.vbanned_rights()); + _by = peerToUser(peerFromUser(data.vkicked_by())); + + _type = (_restrictions.flags & ChatRestriction::ViewMessages) + ? Type::Banned + : Type::Restricted; + }, [&](const MTPDchannelParticipantLeft &data) { + _type = Type::Left; + }); +} + +ChatParticipant::ChatParticipant( + Type type, + PeerId peerId, + UserId by, + ChatRestrictionsInfo restrictions, + ChatAdminRightsInfo rights, + bool canBeEdited, + QString rank) +: _type(type) +, _peer(peerId) +, _by(by) +, _canBeEdited(canBeEdited) +, _rank(rank) +, _restrictions(std::move(restrictions)) +, _rights(std::move(rights)) { +} + +void ChatParticipant::tryApplyCreatorTo( + not_null<ChannelData*> channel) const { + if (isCreator() && isUser()) { + if (const auto info = channel->mgInfo.get()) { + info->creator = channel->owner().userLoaded(userId()); + info->creatorRank = rank(); + } + } +} + +bool ChatParticipant::isUser() const { + return peerIsUser(_peer); +} + +bool ChatParticipant::isCreator() const { + return _type == Type::Creator; +} + +bool ChatParticipant::isCreatorOrAdmin() const { + return _type == Type::Creator || _type == Type::Admin; +} + +bool ChatParticipant::isKicked() const { + return _type == Type::Banned; +} + +bool ChatParticipant::canBeEdited() const { + return _canBeEdited; +} + +UserId ChatParticipant::by() const { + return _by; +} + +PeerId ChatParticipant::id() const { + return _peer; +} + +UserId ChatParticipant::userId() const { + return peerToUser(_peer); +} + +ChatRestrictionsInfo ChatParticipant::restrictions() const { + return _restrictions; +} + +ChatAdminRightsInfo ChatParticipant::rights() const { + return _rights; +} + +ChatParticipant::Type ChatParticipant::type() const { + return _type; +} + +QString ChatParticipant::rank() const { + return _rank; +} + ChatParticipants::ChatParticipants(not_null<ApiWrap*> api) : _api(&api->instance()) { } diff --git a/Telegram/SourceFiles/api/api_chat_participants.h b/Telegram/SourceFiles/api/api_chat_participants.h index be07fad94..ce4354aa1 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.h +++ b/Telegram/SourceFiles/api/api_chat_participants.h @@ -7,16 +7,69 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "data/data_chat_participant_status.h" #include "mtproto/sender.h" #include "base/timer.h" class ApiWrap; class ChannelData; -struct ChatRestrictionsInfo; - namespace Api { +class ChatParticipant final { +public: + enum class Type { + Creator, + Admin, + Member, + Restricted, + Left, + Banned, + }; + + explicit ChatParticipant( + const MTPChannelParticipant &p, + not_null<PeerData*> peer); + ChatParticipant( + Type type, + PeerId peerId, + UserId by, + ChatRestrictionsInfo restrictions, + ChatAdminRightsInfo rights, + bool canBeEdited = false, + QString rank = QString()); + + bool isUser() const; + bool isCreator() const; + bool isCreatorOrAdmin() const; + bool isKicked() const; + bool canBeEdited() const; + + UserId by() const; + PeerId id() const; + UserId userId() const; + + ChatRestrictionsInfo restrictions() const; + ChatAdminRightsInfo rights() const; + + Type type() const; + QString rank() const; + + void tryApplyCreatorTo(not_null<ChannelData*> channel) const; +private: + Type _type = Type::Member; + + PeerId _peer; + UserId _by; // Banned/Restricted/Promoted. + + bool _canBeEdited = false; + + QString _rank; + + ChatRestrictionsInfo _restrictions; + ChatAdminRightsInfo _rights; +}; + class ChatParticipants final { public: using TLMembers = MTPchannels_ChannelParticipants; From f25557307088ccc320a354982a43ff2bb6e85f96 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 24 Nov 2021 07:25:05 +0300 Subject: [PATCH 092/180] Replaced use of raw MTP* participant type with new transitional class. --- .../SourceFiles/api/api_chat_participants.cpp | 215 +++++++----------- .../SourceFiles/api/api_chat_participants.h | 19 +- .../boxes/peers/add_participants_box.cpp | 29 ++- .../boxes/peers/edit_participants_box.cpp | 195 ++++++++-------- .../boxes/peers/edit_participants_box.h | 16 +- .../admin_log/history_admin_log_inner.cpp | 55 ++--- .../SourceFiles/window/window_peer_menu.cpp | 44 ++-- 7 files changed, 249 insertions(+), 324 deletions(-) diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index d6d4404c2..ee5c9da64 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -23,6 +23,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Api { namespace { +using Members = ChatParticipants::Members; + constexpr auto kSmallDelayMs = crl::time(5); // 1 second wait before reload members in channel after adding. @@ -35,57 +37,32 @@ constexpr auto kMaxUsersPerInvite = 100; // that was added to this chat. constexpr auto kForwardMessagesOnAdd = 100; -[[nodiscard]] PeerId TLToPeerId(const MTPChannelParticipant &p) { - return p.match([](const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); +std::vector<ChatParticipant> ParseList( + const ChatParticipants::TLMembers &data, + not_null<PeerData*> peer) { + return ranges::views::all( + data.vparticipants().v + ) | ranges::views::transform([&](const MTPChannelParticipant &p) { + return ChatParticipant(p, peer); + }) | ranges::to_vector; } -void ApplyMegagroupAdmins( - not_null<ChannelData*> channel, - const MTPDchannels_channelParticipants &data) { +void ApplyMegagroupAdmins(not_null<ChannelData*> channel, Members list) { Expects(channel->isMegagroup()); - channel->owner().processUsers(data.vusers()); - - const auto &list = data.vparticipants().v; - const auto i = ranges::find( - list, - mtpc_channelParticipantCreator, - &MTPChannelParticipant::type); + const auto i = ranges::find_if(list, &Api::ChatParticipant::isCreator); if (i != list.end()) { - const auto &data = i->c_channelParticipantCreator(); - const auto userId = data.vuser_id().v; - channel->mgInfo->creator = channel->owner().userLoaded(userId); - channel->mgInfo->creatorRank = qs(data.vrank().value_or_empty()); + i->tryApplyCreatorTo(channel); } else { channel->mgInfo->creator = nullptr; channel->mgInfo->creatorRank = QString(); } auto adding = base::flat_map<UserId, QString>(); - auto admins = ranges::make_subrange( - list.begin(), list.end() - ) | ranges::views::transform([](const MTPChannelParticipant &p) { - const auto participantId = TLToPeerId(p); - const auto rank = p.match([](const MTPDchannelParticipantAdmin &data) { - return qs(data.vrank().value_or_empty()); - }, [](const MTPDchannelParticipantCreator &data) { - return qs(data.vrank().value_or_empty()); - }, [](const auto &data) { - return QString(); - }); - return std::make_pair(participantId, rank); - }) | ranges::views::filter([](const auto &pair) { - return peerIsUser(pair.first); - }); - for (const auto &[participantId, rank] : admins) { - Assert(peerIsUser(participantId)); - adding.emplace(peerToUser(participantId), rank); + for (const auto &p : list) { + if (p.isUser()) { + adding.emplace(p.userId(), p.rank()); + } } if (channel->mgInfo->creator) { adding.emplace( @@ -113,34 +90,24 @@ void ApplyMegagroupAdmins( void RefreshChannelAdmins( not_null<ChannelData*> channel, - ChatParticipants::TLMembersList participants) { + Members participants) { Data::ChannelAdminChanges changes(channel); for (const auto &p : participants) { - const auto participantId = TLToPeerId(p); - const auto userId = peerToUser(participantId); - p.match([&](const MTPDchannelParticipantAdmin &data) { - Assert(peerIsUser(participantId)); - changes.add(userId, qs(data.vrank().value_or_empty())); - }, [&](const MTPDchannelParticipantCreator &data) { - Assert(peerIsUser(participantId)); - const auto rank = qs(data.vrank().value_or_empty()); - if (const auto info = channel->mgInfo.get()) { - info->creator = channel->owner().userLoaded(userId); - info->creatorRank = rank; + if (p.isUser()) { + if (p.isCreatorOrAdmin()) { + p.tryApplyCreatorTo(channel); + changes.add(p.userId(), p.rank()); + } else { + changes.remove(p.userId()); } - changes.add(userId, rank); - }, [&](const auto &data) { - if (userId) { - changes.remove(userId); - } - }); + } } } void ApplyLastList( not_null<ChannelData*> channel, int availableCount, - ChatParticipants::TLMembersList list) { + Members list) { channel->mgInfo->lastAdmins.clear(); channel->mgInfo->lastRestricted.clear(); channel->mgInfo->lastParticipants.clear(); @@ -150,36 +117,15 @@ void ApplyLastList( auto botStatus = channel->mgInfo->botStatus; for (const auto &p : list) { - const auto participantId = TLToPeerId(p); - if (!participantId) { - continue; - } - const auto participant = channel->owner().peer(participantId); + const auto participant = channel->owner().peer(p.id()); const auto user = participant->asUser(); - const auto adminCanEdit = (p.type() == mtpc_channelParticipantAdmin) - ? p.c_channelParticipantAdmin().is_can_edit() - : (p.type() == mtpc_channelParticipantCreator) - ? channel->amCreator() - : false; - const auto adminRights = (p.type() == mtpc_channelParticipantAdmin) - ? ChatAdminRightsInfo(p.c_channelParticipantAdmin().vadmin_rights()) - : (p.type() == mtpc_channelParticipantCreator) - ? ChatAdminRightsInfo(p.c_channelParticipantCreator().vadmin_rights()) - : ChatAdminRightsInfo(); - const auto restrictedRights = (p.type() == mtpc_channelParticipantBanned) - ? ChatRestrictionsInfo( - p.c_channelParticipantBanned().vbanned_rights()) - : ChatRestrictionsInfo(); - if (p.type() == mtpc_channelParticipantCreator) { + const auto adminRights = p.rights(); + const auto restrictedRights = p.restrictions(); + if (p.isCreator()) { Assert(user != nullptr); - const auto &creator = p.c_channelParticipantCreator(); - const auto rank = qs(creator.vrank().value_or_empty()); - channel->mgInfo->creator = user; - channel->mgInfo->creatorRank = rank; + p.tryApplyCreatorTo(channel); if (!channel->mgInfo->admins.empty()) { - Data::ChannelAdminChanges(channel).add( - peerToUser(participantId), - rank); + Data::ChannelAdminChanges(channel).add(p.userId(), p.rank()); } } if (user @@ -188,7 +134,7 @@ void ApplyLastList( if (adminRights.flags) { channel->mgInfo->lastAdmins.emplace( user, - MegagroupInfo::Admin{ adminRights, adminCanEdit }); + MegagroupInfo::Admin{ adminRights, p.canBeEdited() }); } else if (restrictedRights.flags) { channel->mgInfo->lastRestricted.emplace( user, @@ -196,7 +142,8 @@ void ApplyLastList( } if (user->isBot()) { channel->mgInfo->bots.insert(user); - if (channel->mgInfo->botStatus != 0 && channel->mgInfo->botStatus < 2) { + if ((channel->mgInfo->botStatus != 0) + && (channel->mgInfo->botStatus < 2)) { channel->mgInfo->botStatus = 2; } } @@ -227,7 +174,7 @@ void ApplyLastList( void ApplyBotsList( not_null<ChannelData*> channel, int availableCount, - ChatParticipants::TLMembersList list) { + Members list) { const auto history = channel->owner().historyLoaded(channel); channel->mgInfo->bots.clear(); channel->mgInfo->botStatus = -1; @@ -236,12 +183,7 @@ void ApplyBotsList( auto botStatus = channel->mgInfo->botStatus; auto keyboardBotFound = !history || !history->lastKeyboardFrom; for (const auto &p : list) { - const auto participantId = TLToPeerId(p); - if (!participantId) { - continue; - } - - const auto participant = channel->owner().peer(participantId); + const auto participant = channel->owner().peer(p.id()); const auto user = participant->asUser(); if (user && user->isBot()) { channel->mgInfo->bots.insert(user); @@ -409,8 +351,14 @@ void ChatParticipants::requestForAdd( MTP_int(channel->session().serverConfig().chatSizeMax), MTP_long(participantsHash) )).done([=](const MTPchannels_ChannelParticipants &result) { - base::take(_forAdd).callback(result); - }).fail([=](const MTP::Error &error) { + result.match([&](const MTPDchannels_channelParticipants &data) { + base::take(_forAdd).callback(data); + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + base::take(_forAdd); + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); + }); + }).fail([=] { base::take(_forAdd); }).send(); } @@ -431,11 +379,13 @@ void ChatParticipants::requestLast(not_null<ChannelData*> channel) { MTP_long(participantsHash) )).done([=](const MTPchannels_ChannelParticipants &result) { _participantsRequests.remove(channel); - parse(channel, result, [&](int availableCount, TLMembersList list) { - ApplyLastList( - channel, - availableCount, - list); + + result.match([&](const MTPDchannels_channelParticipants &data) { + const auto &[availableCount, list] = Parse(channel, data); + ApplyLastList(channel, availableCount, list); + }, [](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); }); }).fail([this, channel](const MTP::Error &error) { _participantsRequests.remove(channel); @@ -459,11 +409,12 @@ void ChatParticipants::requestBots(not_null<ChannelData*> channel) { MTP_long(participantsHash) )).done([=](const MTPchannels_ChannelParticipants &result) { _botsRequests.remove(channel); - parse(channel, result, [&](int availableCount, TLMembersList list) { - ApplyBotsList( - channel, - availableCount, - list); + result.match([&](const MTPDchannels_channelParticipants &data) { + const auto &[availableCount, list] = Parse(channel, data); + ApplyLastList(channel, availableCount, list); + }, [](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); }); }).fail([=](const MTP::Error &error) { _botsRequests.remove(channel); @@ -488,7 +439,8 @@ void ChatParticipants::requestAdmins(not_null<ChannelData*> channel) { )).done([=](const MTPchannels_ChannelParticipants &result) { _adminsRequests.remove(channel); result.match([&](const MTPDchannels_channelParticipants &data) { - ApplyMegagroupAdmins(channel, data); + channel->owner().processUsers(data.vusers()); + ApplyMegagroupAdmins(channel, ParseList(data, channel)); }, [](const MTPDchannels_channelParticipantsNotModified &) { LOG(("API Error: " "channels.channelParticipantsNotModified received!")); @@ -562,40 +514,27 @@ void ChatParticipants::add( } } -void ChatParticipants::parseRecent( +ChatParticipants::Parsed ChatParticipants::Parse( not_null<ChannelData*> channel, - const TLMembers &result, - Fn<void(int availableCount, TLMembersList list)> callbackList) { - parse(channel, result, [&](int availableCount, TLMembersList list) { - const auto applyLast = channel->isMegagroup() - && (channel->mgInfo->lastParticipants.size() <= list.size()); - if (applyLast) { - ApplyLastList( - channel, - availableCount, - list); - } - if (callbackList) { - callbackList(availableCount, list); - } - }); + const TLMembers &data) { + channel->owner().processUsers(data.vusers()); + auto list = ParseList(data, channel); + if (channel->mgInfo) { + RefreshChannelAdmins(channel, list); + } + return { data.vcount().v, std::move(list) }; } -void ChatParticipants::parse( +ChatParticipants::Parsed ChatParticipants::ParseRecent( not_null<ChannelData*> channel, - const TLMembers &result, - Fn<void(int availableCount, TLMembersList list)> callbackList) { - result.match([&](const MTPDchannels_channelParticipants &data) { - channel->owner().processUsers(data.vusers()); - if (channel->mgInfo) { - RefreshChannelAdmins(channel, data.vparticipants().v); - } - if (callbackList) { - callbackList(data.vcount().v, data.vparticipants().v); - } - }, [&](const MTPDchannels_channelParticipantsNotModified &) { - LOG(("API Error: channels.channelParticipantsNotModified received!")); - }); + const TLMembers &data) { + const auto result = Parse(channel, data); + const auto applyLast = channel->isMegagroup() + && (channel->mgInfo->lastParticipants.size() <= result.list.size()); + if (applyLast) { + ApplyLastList(channel, result.availableCount, result.list); + } + return result; } void ChatParticipants::requestSelf(not_null<ChannelData*> channel) { diff --git a/Telegram/SourceFiles/api/api_chat_participants.h b/Telegram/SourceFiles/api/api_chat_participants.h index ce4354aa1..70e5eafa5 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.h +++ b/Telegram/SourceFiles/api/api_chat_participants.h @@ -72,8 +72,13 @@ private: class ChatParticipants final { public: - using TLMembers = MTPchannels_ChannelParticipants; - using TLMembersList = const QVector<MTPChannelParticipant> &; + struct Parsed { + const int availableCount; + const std::vector<ChatParticipant> list; + }; + + using TLMembers = MTPDchannels_channelParticipants; + using Members = const std::vector<ChatParticipant> &; explicit ChatParticipants(not_null<ApiWrap*> api); void requestLast(not_null<ChannelData*> channel); @@ -81,14 +86,12 @@ public: void requestAdmins(not_null<ChannelData*> channel); void requestCountDelayed(not_null<ChannelData*> channel); - void parse( + static Parsed Parse( not_null<ChannelData*> channel, - const TLMembers &result, - Fn<void(int availableCount, TLMembersList list)> callbackList); - void parseRecent( + const TLMembers &data); + static Parsed ParseRecent( not_null<ChannelData*> channel, - const TLMembers &result, - Fn<void(int availableCount, TLMembersList)> callbackList = nullptr); + const TLMembers &data); void add( not_null<PeerData*> peer, const std::vector<not_null<UserData*>> &users, diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index e12ff3d1a..edaa07d05 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -466,9 +466,10 @@ void AddSpecialBoxController::loadMoreRows() { )).done([=](const MTPchannels_ChannelParticipants &result) { _loadRequestId = 0; auto &session = channel->session(); - session.api().chatParticipants().parse(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { + result.match([&](const MTPDchannels_channelParticipants &data) { + const auto &[availableCount, list] = Api::ChatParticipants::Parse( + channel, + data); for (const auto &data : list) { if (const auto participant = _additional.applyParticipant( data)) { @@ -481,8 +482,9 @@ void AddSpecialBoxController::loadMoreRows() { // To be sure - wait for a whole empty result list. _allLoaded = true; } + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: channels.channelParticipantsNotModified received!")); }); - if (delegate()->peerListFullRowsCount() > 0) { setDescriptionText(QString()); } else if (_allLoaded) { @@ -525,7 +527,8 @@ bool AddSpecialBoxController::checkInfoLoaded( )).done([=](const MTPchannels_ChannelParticipant &result) { result.match([&](const MTPDchannels_channelParticipant &data) { channel->owner().processUsers(data.vusers()); - _additional.applyParticipant(data.vparticipant()); + _additional.applyParticipant( + Api::ChatParticipant(data.vparticipant(), channel)); }); callback(); }).fail([=](const MTP::Error &error) { @@ -966,7 +969,7 @@ void AddSpecialBoxSearchController::searchParticipantsDone( const auto channel = _peer->asChannel(); auto query = _query; if (requestId) { - const auto addToCache = [&](auto&&...) { + const auto addToCache = [&] { auto it = _participantsQueries.find(requestId); if (it != _participantsQueries.cend()) { query = it->second.text; @@ -978,10 +981,13 @@ void AddSpecialBoxSearchController::searchParticipantsDone( _participantsQueries.erase(it); } }; - channel->session().api().chatParticipants().parse( - channel, - result, - addToCache); + result.match([&](const MTPDchannels_channelParticipants &data) { + Api::ChatParticipants::Parse(channel, data); + addToCache(); + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); + }); } if (_requestId != requestId) { @@ -1001,7 +1007,8 @@ void AddSpecialBoxSearchController::searchParticipantsDone( } } for (const auto &data : list) { - if (const auto user = _additional->applyParticipant(data)) { + if (const auto user = _additional->applyParticipant( + Api::ChatParticipant(data, channel))) { delegate()->peerListSearchAddRow(user); } } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 92bf2a79a..7658449a5 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -537,34 +537,33 @@ void ParticipantsAdditionalData::applyAdminLocally( const QString &rank) { const auto date = base::unixtime::now(); // Incorrect, but ignored. if (isCreator(user) && user->isSelf()) { - using Flag = MTPDchannelParticipantCreator::Flag; - applyParticipant(MTP_channelParticipantCreator( - MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank), - peerToBareMTPInt(user->id), - MTP_chatAdminRights( - MTP_flags(MTPDchatAdminRights::Flags::from_raw( - uint32(rights.flags)))), - MTP_string(rank))); + applyParticipant(Api::ChatParticipant( + Api::ChatParticipant::Type::Creator, + user->id, + UserId(), + ChatRestrictionsInfo(), + std::move(rights), + true, // As the creator is self. + rank)); } else if (!rights.flags) { - applyParticipant(MTP_channelParticipant( - peerToBareMTPInt(user->id), - MTP_int(date))); + applyParticipant(Api::ChatParticipant( + Api::ChatParticipant::Type::Member, + user->id, + UserId(), + ChatRestrictionsInfo(), + ChatAdminRightsInfo())); } else { - using Flag = MTPDchannelParticipantAdmin::Flag; const auto alreadyPromotedBy = adminPromotedBy(user); - applyParticipant(MTP_channelParticipantAdmin( - MTP_flags(Flag::f_can_edit - | (rank.isEmpty() ? Flag(0) : Flag::f_rank)), - peerToBareMTPInt(user->id), - MTPlong(), // inviter_id - peerToBareMTPInt(alreadyPromotedBy - ? alreadyPromotedBy->id - : user->session().userPeerId()), - MTP_int(date), - MTP_chatAdminRights( - MTP_flags(MTPDchatAdminRights::Flags::from_raw( - uint32(rights.flags)))), - MTP_string(rank))); + applyParticipant(Api::ChatParticipant( + Api::ChatParticipant::Type::Admin, + user->id, + alreadyPromotedBy + ? peerToUser(alreadyPromotedBy->id) + : user->session().userId(), + ChatRestrictionsInfo(), + std::move(rights), + true, + rank)); } } @@ -575,76 +574,73 @@ void ParticipantsAdditionalData::applyBannedLocally( const auto date = base::unixtime::now(); // Incorrect, but ignored. if (!rights.flags) { if (user) { - applyParticipant(MTP_channelParticipant( - peerToBareMTPInt(user->id), - MTP_int(date))); + applyParticipant(Api::ChatParticipant( + Api::ChatParticipant::Type::Member, + user->id, + UserId(), + ChatRestrictionsInfo(), + ChatAdminRightsInfo())); } else { setExternal(participant); } } else { const auto kicked = rights.flags & ChatRestriction::ViewMessages; - const auto alreadyRestrictedBy = restrictedBy( - participant); - applyParticipant(MTP_channelParticipantBanned( - MTP_flags(kicked - ? MTPDchannelParticipantBanned::Flag::f_left - : MTPDchannelParticipantBanned::Flag(0)), - peerToMTP(participant->id), - peerToBareMTPInt(alreadyRestrictedBy - ? alreadyRestrictedBy->id - : participant->session().userPeerId()), - MTP_int(date), - MTP_chatBannedRights( - MTP_flags(MTPDchatBannedRights::Flags::from_raw( - uint32(rights.flags))), - MTP_int(rights.until)))); + const auto alreadyRestrictedBy = restrictedBy(participant); + applyParticipant(Api::ChatParticipant( + kicked + ? Api::ChatParticipant::Type::Banned + : Api::ChatParticipant::Type::Restricted, + participant->id, + alreadyRestrictedBy + ? peerToUser(alreadyRestrictedBy->id) + : participant->session().userId(), + std::move(rights), + ChatAdminRightsInfo())); } } PeerData *ParticipantsAdditionalData::applyParticipant( - const MTPChannelParticipant &data) { + const Api::ChatParticipant &data) { return applyParticipant(data, _role); } PeerData *ParticipantsAdditionalData::applyParticipant( - const MTPChannelParticipant &data, + const Api::ChatParticipant &data, Role overrideRole) { const auto logBad = [&]() -> PeerData* { LOG(("API Error: Bad participant type %1 got " "while requesting for participants, role: %2" - ).arg(data.type() + ).arg(static_cast<int>(data.type()) ).arg(static_cast<int>(overrideRole))); return nullptr; }; - return data.match([&]( - const MTPDchannelParticipantCreator &data) -> PeerData* { + switch (data.type()) { + case Api::ChatParticipant::Type::Creator: { if (overrideRole != Role::Profile && overrideRole != Role::Members && overrideRole != Role::Admins) { return logBad(); } return applyCreator(data); - }, [&](const MTPDchannelParticipantAdmin &data) -> PeerData* { + } + case Api::ChatParticipant::Type::Admin: { if (overrideRole != Role::Profile && overrideRole != Role::Members && overrideRole != Role::Admins) { return logBad(); } return applyAdmin(data); - }, [&](const MTPDchannelParticipantSelf &data) -> PeerData* { + } + case Api::ChatParticipant::Type::Member: { if (overrideRole != Role::Profile && overrideRole != Role::Members) { return logBad(); } - return applyRegular(data.vuser_id()); - }, [&](const MTPDchannelParticipant &data) -> PeerData* { - if (overrideRole != Role::Profile - && overrideRole != Role::Members) { - return logBad(); - } - return applyRegular(data.vuser_id()); - }, [&](const MTPDchannelParticipantBanned &data) { + return applyRegular(data.userId()); + } + case Api::ChatParticipant::Type::Restricted: + case Api::ChatParticipant::Type::Banned: if (overrideRole != Role::Profile && overrideRole != Role::Members && overrideRole != Role::Restricted @@ -652,23 +648,23 @@ PeerData *ParticipantsAdditionalData::applyParticipant( return logBad(); } return applyBanned(data); - }, [&](const MTPDchannelParticipantLeft &data) { + case Api::ChatParticipant::Type::Left: return logBad(); - }); + }; } UserData *ParticipantsAdditionalData::applyCreator( - const MTPDchannelParticipantCreator &data) { - if (const auto user = applyRegular(data.vuser_id())) { + const Api::ChatParticipant &data) { + if (const auto user = applyRegular(data.userId())) { _creator = user; - _adminRights[user] = ChatAdminRightsInfo(data.vadmin_rights()); + _adminRights[user] = data.rights(); if (user->isSelf()) { _adminCanEdit.emplace(user); } else { _adminCanEdit.erase(user); } - if (const auto rank = data.vrank()) { - _adminRanks[user] = qs(*rank); + if (data.rank().isEmpty()) { + _adminRanks[user] = data.rank(); } else { _adminRanks.remove(user); } @@ -678,8 +674,8 @@ UserData *ParticipantsAdditionalData::applyCreator( } UserData *ParticipantsAdditionalData::applyAdmin( - const MTPDchannelParticipantAdmin &data) { - const auto user = _peer->owner().userLoaded(UserId(data.vuser_id().v)); + const Api::ChatParticipant &data) { + const auto user = _peer->owner().userLoaded(data.userId()); if (!user) { return nullptr; } else if (const auto chat = _peer->asChat()) { @@ -692,18 +688,18 @@ UserData *ParticipantsAdditionalData::applyAdmin( _restrictedRights.erase(user); _kicked.erase(user); _restrictedBy.erase(user); - _adminRights[user] = ChatAdminRightsInfo(data.vadmin_rights()); - if (data.is_can_edit()) { + _adminRights[user] = data.rights(); + if (data.canBeEdited()) { _adminCanEdit.emplace(user); } else { _adminCanEdit.erase(user); } - if (const auto rank = data.vrank()) { - _adminRanks[user] = qs(*rank); + if (data.rank().isEmpty()) { + _adminRanks[user] = data.rank(); } else { _adminRanks.remove(user); } - if (const auto by = _peer->owner().userLoaded(data.vpromoted_by())) { + if (const auto by = _peer->owner().userLoaded(data.by())) { const auto i = _adminPromotedBy.find(user); if (i == _adminPromotedBy.end()) { _adminPromotedBy.emplace(user, by); @@ -712,12 +708,12 @@ UserData *ParticipantsAdditionalData::applyAdmin( } } else { LOG(("API Error: No user %1 for admin promoted by." - ).arg(data.vpromoted_by().v)); + ).arg(data.by().bare)); } return user; } -UserData *ParticipantsAdditionalData::applyRegular(MTPlong userId) { +UserData *ParticipantsAdditionalData::applyRegular(UserId userId) { const auto user = _peer->owner().userLoaded(userId); if (!user) { return nullptr; @@ -738,9 +734,8 @@ UserData *ParticipantsAdditionalData::applyRegular(MTPlong userId) { } PeerData *ParticipantsAdditionalData::applyBanned( - const MTPDchannelParticipantBanned &data) { - const auto participant = _peer->owner().peerLoaded( - peerFromMTP(data.vpeer())); + const Api::ChatParticipant &data) { + const auto participant = _peer->owner().peerLoaded(data.id()); if (!participant) { return nullptr; } @@ -751,14 +746,13 @@ PeerData *ParticipantsAdditionalData::applyBanned( _adminCanEdit.erase(user); _adminPromotedBy.erase(user); } - if (data.is_left()) { + if (data.isKicked()) { _kicked.emplace(participant); } else { _kicked.erase(participant); } - _restrictedRights[participant] = ChatRestrictionsInfo( - data.vbanned_rights()); - if (const auto by = _peer->owner().userLoaded(data.vkicked_by())) { + _restrictedRights[participant] = data.restrictions(); + if (const auto by = _peer->owner().userLoaded(data.by())) { const auto i = _restrictedBy.find(participant); if (i == _restrictedBy.end()) { _restrictedBy.emplace(participant, by); @@ -1395,17 +1389,11 @@ void ParticipantsBoxController::loadMoreRows() { auto wasRecentRequest = firstLoad && (_role == Role::Members || _role == Role::Profile); - auto parseParticipants = [&](auto &&result, auto &&callback) { - auto &api = channel->session().api().chatParticipants(); - if (wasRecentRequest) { - api.parseRecent(channel, result, callback); - } else { - api.parse(channel, result, callback); - } - }; - parseParticipants(result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { + + result.match([&](const MTPDchannels_channelParticipants &data) { + const auto &[availableCount, list] = wasRecentRequest + ? Api::ChatParticipants::ParseRecent(channel, data) + : Api::ChatParticipants::Parse(channel, data); for (const auto &data : list) { if (const auto participant = _additional.applyParticipant( data)) { @@ -1418,6 +1406,9 @@ void ParticipantsBoxController::loadMoreRows() { // To be sure - wait for a whole empty result list. _allLoaded = true; } + }, [](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); }); if (_allLoaded @@ -2011,7 +2002,12 @@ void ParticipantsBoxController::subscribeToCreatorChange( channel->mgInfo->lastAdmins.clear(); channel->mgInfo->lastRestricted.clear(); channel->mgInfo->lastParticipants.clear(); - api->chatParticipants().parseRecent(channel, result); + + result.match([&](const MTPDchannels_channelParticipants &data) { + Api::ChatParticipants::ParseRecent(channel, data); + }, [](const MTPDchannels_channelParticipantsNotModified &) { + }); + if (weak) { fullListRefresh(); } @@ -2176,10 +2172,13 @@ void ParticipantsBoxSearchController::searchDone( _queries.erase(it); } }; - _channel->session().api().chatParticipants().parse( - _channel, - result, - addToCache); + result.match([&](const MTPDchannels_channelParticipants &data) { + Api::ChatParticipants::Parse(_channel, data); + addToCache(); + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: " + "channels.channelParticipantsNotModified received!")); + }); } if (_requestId != requestId) { return; @@ -2199,7 +2198,7 @@ void ParticipantsBoxSearchController::searchDone( : _role; for (const auto &data : list) { const auto user = _additional->applyParticipant( - data, + Api::ChatParticipant(data, _channel), overrideRole); if (user) { delegate()->peerListSearchAddRow(user); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h index 9bafc2b53..cfbb5fbf3 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.h @@ -21,6 +21,10 @@ namespace Window { class SessionNavigation; } // namespace Window +namespace Api { +class ChatParticipant; +} // namespace Api + Fn<void( ChatAdminRightsInfo oldRights, ChatAdminRightsInfo newRights, @@ -80,9 +84,9 @@ public: ParticipantsAdditionalData(not_null<PeerData*> peer, Role role); - PeerData *applyParticipant(const MTPChannelParticipant &data); + PeerData *applyParticipant(const Api::ChatParticipant &data); PeerData *applyParticipant( - const MTPChannelParticipant &data, + const Api::ChatParticipant &data, Role overrideRole); void setExternal(not_null<PeerData*> participant); void checkForLoaded(not_null<PeerData*> participant); @@ -117,10 +121,10 @@ public: ChatRestrictionsInfo rights); private: - UserData *applyCreator(const MTPDchannelParticipantCreator &data); - UserData *applyAdmin(const MTPDchannelParticipantAdmin &data); - UserData *applyRegular(MTPlong userId); - PeerData *applyBanned(const MTPDchannelParticipantBanned &data); + UserData *applyCreator(const Api::ChatParticipant &data); + UserData *applyAdmin(const Api::ChatParticipant &data); + UserData *applyRegular(UserId userId); + PeerData *applyBanned(const Api::ChatParticipant &data); void fillFromChat(not_null<ChatData*> chat); void fillFromChannel(not_null<ChannelData*> channel); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 2e932c38a..3ecd53f4e 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -445,44 +445,27 @@ void InnerWidget::requestAdmins() { MTP_int(kMaxChannelAdmins), MTP_long(participantsHash) )).done([=](const MTPchannels_ChannelParticipants &result) { - session().api().chatParticipants().parse(_channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - auto filtered = ( - list - ) | ranges::views::transform([&](const MTPChannelParticipant &p) { - const auto participantId = p.match([]( - const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); - const auto canEdit = p.match([]( - const MTPDchannelParticipantAdmin &data) { - return data.is_can_edit(); - }, [](const auto &) { - return false; - }); - return std::make_pair(participantId, canEdit); - }) | ranges::views::transform([&](auto &&pair) { - return std::make_pair( - (peerIsUser(pair.first) - ? session().data().userLoaded( - peerToUser(pair.first)) - : nullptr), - pair.second); - }) | ranges::views::filter([&](auto &&pair) { - return (pair.first != nullptr); - }); - - for (auto [user, canEdit] : filtered) { - _admins.emplace_back(user); - if (canEdit) { - _adminsCanEdit.emplace_back(user); + result.match([&](const MTPDchannels_channelParticipants &data) { + const auto &[availableCount, list] = Api::ChatParticipants::Parse( + _channel, + data); + _admins.clear(); + _adminsCanEdit.clear(); + for (const auto &parsed : list) { + if (parsed.isUser()) { + const auto user = _channel->owner().userLoaded( + parsed.userId()); + if (user) { + _admins.emplace_back(user); + if (parsed.canBeEdited() && !parsed.isCreator()) { + _adminsCanEdit.emplace_back(user); + } + } } } + }, [&](const MTPDchannels_channelParticipantsNotModified &) { + LOG(("API Error: c" + "hannels.channelParticipantsNotModified received!")); }); if (_admins.empty()) { _admins.push_back(session().user()); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index d70773c30..b329eab3c 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -1092,34 +1092,24 @@ void PeerMenuAddChannelMembers( } const auto api = &channel->session().api(); api->chatParticipants().requestForAdd(channel, crl::guard(navigation, [=]( - const MTPchannels_ChannelParticipants &result) { - api->chatParticipants().parse(channel, result, [&]( - int availableCount, - const QVector<MTPChannelParticipant> &list) { - auto already = ( - list - ) | ranges::views::transform([](const MTPChannelParticipant &p) { - return p.match([](const MTPDchannelParticipantBanned &data) { - return peerFromMTP(data.vpeer()); - }, [](const MTPDchannelParticipantLeft &data) { - return peerFromMTP(data.vpeer()); - }, [](const auto &data) { - return peerFromUser(data.vuser_id()); - }); - }) | ranges::views::transform([&](PeerId participantId) { - return peerIsUser(participantId) - ? channel->owner().userLoaded( - peerToUser(participantId)) - : nullptr; - }) | ranges::views::filter([](UserData *user) { - return (user != nullptr); - }) | ranges::to_vector; + const Api::ChatParticipants::TLMembers &data) { + const auto &[availableCount, list] = Api::ChatParticipants::Parse( + channel, + data); + const auto already = ( + list + ) | ranges::views::transform([&](const Api::ChatParticipant &p) { + return p.isUser() + ? channel->owner().userLoaded(p.userId()) + : nullptr; + }) | ranges::views::filter([](UserData *user) { + return (user != nullptr); + }) | ranges::to_vector; - AddParticipantsBoxController::Start( - navigation, - channel, - { already.begin(), already.end() }); - }); + AddParticipantsBoxController::Start( + navigation, + channel, + { already.begin(), already.end() }); })); } From e2fbcd4b0e9d434ba12dd35bed553ee62f3a399c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 24 Nov 2021 07:25:05 +0300 Subject: [PATCH 093/180] Replaced parsing of MTPChannelParticipant in AdminLog::GenerateItems. --- .../admin_log/history_admin_log_item.cpp | 132 +++++++++--------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 3d21438f5..9969ad409 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_service.h" #include "history/history_message.h" #include "history/history.h" +#include "api/api_chat_participants.h" #include "api/api_text_entities.h" #include "data/data_channel.h" #include "data/data_user.h" @@ -454,33 +455,34 @@ auto GenerateParticipantString( Ui::Text::WithEntities); } -auto GenerateParticipantChangeTextInner( +auto GenerateParticipantChangeText( not_null<ChannelData*> channel, - const MTPChannelParticipant &participant, - const MTPChannelParticipant *oldParticipant) { - const auto oldType = oldParticipant ? oldParticipant->type() : 0; + const Api::ChatParticipant &participant, + std::optional<Api::ChatParticipant> oldParticipant = std::nullopt) { + using Type = Api::ChatParticipant::Type; + const auto oldRights = oldParticipant + ? oldParticipant->rights() + : ChatAdminRightsInfo(); + const auto oldRestrictions = oldParticipant + ? oldParticipant->restrictions() + : ChatRestrictionsInfo(); + const auto generateOther = [&](PeerId participantId) { auto user = GenerateParticipantString( &channel->session(), participantId); - if (oldType == mtpc_channelParticipantAdmin) { + if (oldParticipant && oldParticipant->type() == Type::Admin) { return GenerateAdminChangeText( channel, user, ChatAdminRightsInfo(), - ChatAdminRightsInfo( - oldParticipant - ->c_channelParticipantAdmin() - .vadmin_rights())); - } else if (oldType == mtpc_channelParticipantBanned) { + oldRights); + } else if (oldParticipant && oldParticipant->type() == Type::Banned) { return GenerateBannedChangeText( participantId, user, ChatRestrictionsInfo(), - ChatRestrictionsInfo( - oldParticipant - ->c_channelParticipantBanned() - .vbanned_rights())); + oldRestrictions); } return tr::lng_admin_log_invited( tr::now, @@ -488,62 +490,62 @@ auto GenerateParticipantChangeTextInner( user, Ui::Text::WithEntities); }; - return participant.match([&](const MTPDchannelParticipantCreator &data) { - // No valid string here :( - return tr::lng_admin_log_transferred( - tr::now, - lt_user, - GenerateParticipantString( + + auto result = [&] { + const auto &peerId = participant.id(); + switch (participant.type()) { + case Api::ChatParticipant::Type::Creator: { + // No valid string here :( + return tr::lng_admin_log_transferred( + tr::now, + lt_user, + GenerateParticipantString(&channel->session(), peerId), + Ui::Text::WithEntities); + } + case Api::ChatParticipant::Type::Admin: { + const auto user = GenerateParticipantString( &channel->session(), - peerFromUser(data.vuser_id())), - Ui::Text::WithEntities); - }, [&](const MTPDchannelParticipantAdmin &data) { - const auto user = GenerateParticipantString( - &channel->session(), - peerFromUser(data.vuser_id())); - return GenerateAdminChangeText( - channel, - user, - ChatAdminRightsInfo(data.vadmin_rights()), - (oldType == mtpc_channelParticipantAdmin - ? ChatAdminRightsInfo( - oldParticipant - ->c_channelParticipantAdmin() - .vadmin_rights()) - : ChatAdminRightsInfo())); - }, [&](const MTPDchannelParticipantBanned &data) { - const auto participantId = peerFromMTP(data.vpeer()); - const auto user = GenerateParticipantString( - &channel->session(), - participantId); - return GenerateBannedChangeText( - participantId, - user, - ChatRestrictionsInfo(data.vbanned_rights()), - (oldType == mtpc_channelParticipantBanned - ? ChatRestrictionsInfo( - oldParticipant - ->c_channelParticipantBanned() - .vbanned_rights()) - : ChatRestrictionsInfo())); - }, [&](const MTPDchannelParticipantLeft &data) { - return generateOther(peerFromMTP(data.vpeer())); - }, [&](const auto &data) { - return generateOther(peerFromUser(data.vuser_id())); - }); + peerId); + return GenerateAdminChangeText( + channel, + user, + participant.rights(), + oldRights); + } + case Api::ChatParticipant::Type::Restricted: + case Api::ChatParticipant::Type::Banned: { + const auto user = GenerateParticipantString( + &channel->session(), + peerId); + return GenerateBannedChangeText( + peerId, + user, + participant.restrictions(), + oldRestrictions); + } + case Api::ChatParticipant::Type::Left: + case Api::ChatParticipant::Type::Member: + return generateOther(peerId); + }; + }(); + + result.entities.push_front( + EntityInText(EntityType::Italic, 0, result.text.size())); + return result; } TextWithEntities GenerateParticipantChangeText( not_null<ChannelData*> channel, const MTPChannelParticipant &participant, - const MTPChannelParticipant *oldParticipant = nullptr) { - auto result = GenerateParticipantChangeTextInner( + std::optional<MTPChannelParticipant>oldParticipant = std::nullopt) { + return GenerateParticipantChangeText( channel, - participant, - oldParticipant); - result.entities.push_front( - EntityInText(EntityType::Italic, 0, result.text.size())); - return result; + Api::ChatParticipant(participant, channel), + oldParticipant + ? std::make_optional(Api::ChatParticipant( + *oldParticipant, + channel)) + : std::nullopt); } TextWithEntities GenerateDefaultBannedRightsChangeText( @@ -918,7 +920,7 @@ void GenerateItems( GenerateParticipantChangeText( channel, action.vnew_participant(), - &action.vprev_participant())); + action.vprev_participant())); }; const auto createParticipantToggleAdmin = [&](const LogPromote &action) { @@ -933,7 +935,7 @@ void GenerateItems( GenerateParticipantChangeText( channel, action.vnew_participant(), - &action.vprev_participant())); + action.vprev_participant())); }; const auto createChangeStickerSet = [&](const LogSticker &action) { From b634ebab78d3e0322faa9927bdcc47f0c2c1e1b9 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 12:02:43 +0400 Subject: [PATCH 094/180] Update API scheme on layer 135. --- Telegram/Resources/tl/api.tl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 4f63ffd6f..f113191df 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -538,7 +538,7 @@ webPagePending#c586da1c id:long date:int = WebPage; webPage#e89c45b2 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page attributes:flags.12?Vector<WebPageAttribute> = WebPage; webPageNotModified#7311ca11 flags:# cached_page_views:flags.0?int = WebPage; -authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; +authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true encrypted_requests_disabled:flags.3?true call_requests_disabled:flags.4?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization; account.authorizations#4bff8ea0 authorization_ttl_days:int authorizations:Vector<Authorization> = account.Authorizations; @@ -1300,7 +1300,7 @@ users.userFull#3b6d152e full_user:UserFull chats:Vector<Chat> users:Vector<User> messages.peerSettings#6880b94d settings:PeerSettings chats:Vector<Chat> users:Vector<User> = messages.PeerSettings; -auth.loggedOut#8b591226 flags:# future_auth_token:flags.0?bytes future_auth_expires:flags.0?int = auth.LoggedOut; +auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes = auth.LoggedOut; ---functions--- @@ -1404,7 +1404,7 @@ account.resetPassword#9308ce1b = account.ResetPasswordResult; account.declinePasswordReset#4c9409f6 = Bool; account.getChatThemes#d638de89 hash:long = account.Themes; account.setAuthorizationTTL#bf899aa0 authorization_ttl_days:int = Bool; -account.changeAuthorizationSettings#432910d5 hash:long encrypted_requests_disabled:Bool = Bool; +account.changeAuthorizationSettings#40f48462 flags:# hash:long encrypted_requests_disabled:flags.0?Bool call_requests_disabled:flags.1?Bool = Bool; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; users.getFullUser#b60f5918 id:InputUser = users.UserFull; From eb6afdf4387e3f092e9dfd902ae60caa9df91387 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 14:23:13 +0400 Subject: [PATCH 095/180] Fix build on Windows. --- Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp | 1 + .../SourceFiles/history/admin_log/history_admin_log_item.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 7658449a5..09b67a480 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -651,6 +651,7 @@ PeerData *ParticipantsAdditionalData::applyParticipant( case Api::ChatParticipant::Type::Left: return logBad(); }; + Unexpected("Api::ChatParticipant::type in applyParticipant."); } UserData *ParticipantsAdditionalData::applyCreator( diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 9969ad409..794719cef 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -527,6 +527,7 @@ auto GenerateParticipantChangeText( case Api::ChatParticipant::Type::Member: return generateOther(peerId); }; + Unexpected("Participant type in GenerateParticipantChangeText."); }(); result.entities.push_front( From c94758609128ab810f212670ecb6ad7b596a6bee Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 15:41:18 +0400 Subject: [PATCH 096/180] Add reverse / shuffle options dropdown. --- Telegram/Resources/langs/lang.strings | 3 + .../media/player/media_player.style | 15 ++ .../media/player/media_player_widget.cpp | 143 ++++++++++++++++-- .../media/player/media_player_widget.h | 8 +- 4 files changed, 154 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d72879dd7..59502c77b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2320,6 +2320,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_player_message_yesterday" = "Yesterday at {time}"; "lng_player_message_date" = "{date} at {time}"; +"lng_audio_player_reverse" = "Reverse order"; +"lng_audio_player_shuffle" = "Shuffle"; + "lng_rights_edit_admin" = "Manage permissions"; "lng_rights_edit_admin_header" = "What can this admin do?"; "lng_rights_edit_admin_rank_name" = "Custom title"; diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 5b75be8c7..46e815a6c 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -286,3 +286,18 @@ mediaPlayerFileLayout: OverviewFileLayout(overviewFileLayout) { mediaPlayerFloatSize: 128px; mediaPlayerFloatMargin: 12px; + +mediaPlayerOrderMenuPosition: point(-2px, -4px); +mediaPlayerOrderMenu: Menu(defaultMenu) { + itemIconPosition: point(13px, 8px); + itemPadding: margins(49px, 9px, 17px, 11px); + itemStyle: boxTextStyle; +} +mediaPlayerOrderMenuActive: Menu(mediaPlayerOrderMenu) { + itemFg: windowActiveTextFg; + itemFgOver: windowActiveTextFg; +} +mediaPlayerOrderIconReverse: icon{{ "player/player_order", windowFg }}; +mediaPlayerOrderIconReverseActive: icon{{ "player/player_order", windowActiveTextFg }}; +mediaPlayerOrderIconShuffle: icon{{ "player/player_shuffle", windowFg }}; +mediaPlayerOrderIconShuffleActive: icon{{ "player/player_shuffle", windowActiveTextFg }}; diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 476a0ccf5..07eb34157 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -18,6 +18,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/dropdown_menu.h" +#include "ui/widgets/menu/menu_action.h" #include "ui/wrap/fade_wrap.h" #include "ui/effects/ripple_animation.h" #include "ui/text/format_values.h" @@ -245,6 +247,7 @@ Widget::Widget( not_null<Window::SessionController*> controller) : RpWidget(parent) , _controller(controller) +, _orderMenuParent(dropdownsParent) , _nameLabel(this, st::mediaPlayerName) , _rightControls(this, object_ptr<Ui::RpWidget>(this)) , _timeLabel(rightControls(), st::mediaPlayerTime) @@ -256,7 +259,7 @@ Widget::Widget( , _close(this, st::mediaPlayerClose) , _shadow(this) , _playbackSlider(this, st::mediaPlayerPlayback) -, _volume(dropdownsParent.get()) +, _volume(std::in_place, dropdownsParent.get()) , _playbackProgress(std::make_unique<View::PlaybackProgress>()) , _speedController( std::make_unique<SpeedController>( @@ -313,6 +316,16 @@ Widget::Widget( updateRepeatToggleIcon(); }, lifetime()); + _orderToggle->setClickedCallback([=] { + showOrderMenu(); + }); + _orderToggle->events( + ) | rpl::filter([=](not_null<QEvent*> e) { + return e->type() == QEvent::Enter; + }) | rpl::start_with_next([=] { + showOrderMenu(); + }, _orderToggle->lifetime()); + Core::App().settings().playerOrderModeValue( ) | rpl::start_with_next([=] { updateOrderToggleIcon(); @@ -362,13 +375,25 @@ Widget::Widget( handleSongUpdate(state); }, lifetime()); - PrepareVolumeDropdown(_volume.data(), controller); - _volumeToggle->installEventFilter(_volume.data()); + PrepareVolumeDropdown(_volume.get(), controller); + _volumeToggle->installEventFilter(_volume.get()); _volume->installEventFilter(this); + hidePlaylistOn(_playPause); + hidePlaylistOn(_close); + setType(AudioMsgId::Type::Song); } +void Widget::hidePlaylistOn(const object_ptr<Ui::IconButton> &button) { + button->events( + ) | rpl::filter([=](not_null<QEvent*> e) { + return (e->type() == QEvent::Enter); + }) | rpl::start_with_next([=] { + updateOverLabelsState(false); + }, button->lifetime()); +} + void Widget::setupRightControls() { const auto raw = rightControls(); raw->paintRequest( @@ -437,14 +462,101 @@ void Widget::showShadowAndDropdowns() { } } +void Widget::showOrderMenu() { + if (_orderMenu) { + return; + } + _orderMenu.emplace(_orderMenuParent); + _orderMenu->installEventFilter(this); + _orderMenu->setHiddenCallback([weak = Ui::MakeWeak(this), menu = _orderMenu.get()] { + menu->deleteLater(); + if (weak && weak->_orderMenu == menu) { + weak->_orderMenu = nullptr; + weak->_orderToggle->setForceRippled(false); + } + }); + _orderMenu->setShowStartCallback(crl::guard(this, [this, menu = _orderMenu.get()] { + if (_orderMenu == menu) { + _orderToggle->setForceRippled(true); + } + })); + _orderMenu->setHideStartCallback(crl::guard(this, [this, menu = _orderMenu.get()] { + if (_orderMenu == menu) { + _orderToggle->setForceRippled(false); + } + })); + _orderToggle->installEventFilter(_orderMenu); + const auto addOrderAction = [&](OrderMode mode) { + struct Fields { + QString label; + const style::icon &icon; + const style::icon &activeIcon; + }; + const auto current = Core::App().settings().playerOrderMode(); + const auto active = (current == mode); + const auto callback = [=] { + Core::App().settings().setPlayerOrderMode(active + ? OrderMode::Default + : mode); + Core::App().saveSettingsDelayed(); + }; + const auto fields = [&]() -> Fields { + switch (mode) { + case OrderMode::Reverse: return { + .label = tr::lng_audio_player_reverse(tr::now), + .icon = st::mediaPlayerOrderIconReverse, + .activeIcon = st::mediaPlayerOrderIconReverseActive, + }; + case OrderMode::Shuffle: return { + .label = tr::lng_audio_player_shuffle(tr::now), + .icon = st::mediaPlayerOrderIconShuffle, + .activeIcon = st::mediaPlayerOrderIconShuffleActive, + }; + } + Unexpected("Order mode in addOrderAction."); + }(); + const auto parent = _orderMenu.get(); + const auto action = Ui::Menu::CreateAction( + parent, + fields.label, + callback); + auto item = base::make_unique_q<Ui::Menu::Action>( + parent, + (active + ? st::mediaPlayerOrderMenuActive + : st::mediaPlayerOrderMenu), + std::move(action), + &(active ? fields.activeIcon : fields.icon), + &(active ? fields.activeIcon : fields.icon)); + _orderMenu->addAction(std::move(item)); + }; + addOrderAction(OrderMode::Reverse); + addOrderAction(OrderMode::Shuffle); + updateDropdownsGeometry(); + _orderMenu->showAnimated(Ui::PanelAnimation::Origin::TopRight); +} + void Widget::updateDropdownsGeometry() { + const auto dropdownWidth = st::mediaPlayerVolumeSize.width(); const auto position = _volume->parentWidget()->mapFromGlobal( _volumeToggle->mapToGlobal( QPoint( - (_volumeToggle->width() - st::mediaPlayerVolumeSize.width()) / 2, + (_volumeToggle->width() - dropdownWidth) / 2, height()))); const auto playerMargins = _volume->getMargin(); - _volume->move(position - QPoint(playerMargins.left(), playerMargins.top())); + const auto shift = QPoint(playerMargins.left(), playerMargins.top()); + _volume->move(position - shift); + + if (_orderMenu) { + const auto position = _orderMenu->parentWidget()->mapFromGlobal( + _orderToggle->mapToGlobal( + QPoint(_orderToggle->width(), _orderToggle->height()))); + const auto padding = st::defaultInnerDropdown.padding; + _orderMenu->move(position + - QPoint(_orderMenu->width(), 0) + + QPoint(padding.right(), -padding.top()) + + st::mediaPlayerOrderMenuPosition); + } } void Widget::hideShadowAndDropdowns() { @@ -516,11 +628,10 @@ void Widget::updateControlsGeometry() { void Widget::updateControlsWrapGeometry() { const auto fade = st::mediaPlayerControlsFade.width(); - rightControls()->resize( - getTimeRight() + _timeLabel->width() + fade, - _repeatToggle->height()); - _rightControls->moveToRight( - st::mediaPlayerCloseRight + _close->width(), + const auto controls = getTimeRight() + _timeLabel->width() + fade; + rightControls()->resize(controls, _repeatToggle->height()); + _rightControls->move( + width() - st::mediaPlayerCloseRight - _close->width() - controls, st::mediaPlayerPlayTop); } @@ -561,6 +672,7 @@ void Widget::markOver(bool over) { _over = true; _wontBeOver = false; updateControlsWrapVisibility(); + updateOverLabelsState(true); } else { _wontBeOver = true; InvokeQueued(this, [=] { @@ -570,8 +682,8 @@ void Widget::markOver(bool over) { _wontBeOver = false; _over = false; updateControlsWrapVisibility(); - updateOverLabelsState(false); }); + updateOverLabelsState(false); } } @@ -705,7 +817,7 @@ void Widget::updateOrderToggleIcon() { &st::mediaPlayerRepeatDisabledRippleBg); break; case OrderMode::Reverse: - _orderToggle->setIconOverride(nullptr); + _orderToggle->setIconOverride(&st::mediaPlayerReverseIcon); _orderToggle->setRippleColorOverride(nullptr); break; case OrderMode::Shuffle: @@ -736,6 +848,7 @@ bool Widget::hasPlaybackSpeedControl() const { void Widget::updateControlsVisibility() { _repeatToggle->setVisible(_type == AudioMsgId::Type::Song); + _orderToggle->setVisible(_type == AudioMsgId::Type::Song); _volumeToggle->setVisible(_type == AudioMsgId::Type::Song); _playbackSpeed->setVisible(hasPlaybackSpeedControl()); if (!_shadow->isHidden()) { @@ -906,13 +1019,15 @@ void Widget::createPrevNextButtons() { _previousTrack.create(this, st::mediaPlayerPreviousButton); _previousTrack->show(); _previousTrack->setClickedCallback([=]() { - instance()->previous(); + instance()->previous(_type); }); _nextTrack.create(this, st::mediaPlayerNextButton); _nextTrack->show(); _nextTrack->setClickedCallback([=]() { - instance()->next(); + instance()->next(_type); }); + hidePlaylistOn(_previousTrack); + hidePlaylistOn(_nextTrack); updatePlayPrevNextPositions(); } } diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 968144386..57346b57d 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -18,6 +18,7 @@ class FlatLabel; class LabelSimple; class IconButton; class PlainShadow; +class DropdownMenu; class FilledSlider; template <typename Widget> class FadeWrap; @@ -85,6 +86,7 @@ private: [[nodiscard]] int getTimeRight() const; void updateOverLabelsState(QPoint pos); void updateOverLabelsState(bool over); + void hidePlaylistOn(const object_ptr<Ui::IconButton> &button); void updatePlayPrevNextPositions(); void updateLabelsGeometry(); @@ -110,7 +112,10 @@ private: void updateTimeLabel(); void markOver(bool over); + void showOrderMenu(); + const not_null<Window::SessionController*> _controller; + const not_null<Ui::RpWidget*> _orderMenuParent; crl::time _seekPositionMs = -1; crl::time _lastDurationMs = 0; @@ -149,7 +154,8 @@ private: object_ptr<Ui::IconButton> _close; object_ptr<Ui::PlainShadow> _shadow = { nullptr }; object_ptr<Ui::FilledSlider> _playbackSlider; - object_ptr<Dropdown> _volume; + base::unique_qptr<Dropdown> _volume; + base::unique_qptr<Ui::DropdownMenu> _orderMenu; std::unique_ptr<View::PlaybackProgress> _playbackProgress; std::unique_ptr<SpeedController> _speedController; From 82e150a27e2e997713adff66b9672edd4ba65dcc Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 15:44:30 +0400 Subject: [PATCH 097/180] Improve device model name handling. --- Telegram/SourceFiles/boxes/sessions_box.cpp | 2 +- Telegram/lib_base | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 3941c5802..aaa433a99 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -34,7 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { constexpr auto kSessionsShortPollTimeout = 60 * crl::time(1000); -constexpr auto kMaxDeviceModelLength = 16; +constexpr auto kMaxDeviceModelLength = 32; void RenameBox(not_null<Ui::GenericBox*> box) { box->setTitle(tr::lng_settings_rename_device_title()); diff --git a/Telegram/lib_base b/Telegram/lib_base index 29ecc271c..e77f08a91 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 29ecc271c04e71eecf88ac2f9ed1c110955e0be9 +Subproject commit e77f08a91a736b479e292092aca7a1ba56516076 From 5cb5e3cc2269a2d5cf950e8a567f41beae366670 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 17:24:23 +0400 Subject: [PATCH 098/180] Use similar dropdown for playback speed. --- .../media/player/media_player.style | 14 +- .../media/player/media_player_widget.cpp | 557 ++++++++++-------- .../media/player/media_player_widget.h | 10 +- Telegram/lib_ui | 2 +- 4 files changed, 313 insertions(+), 270 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 46e815a6c..e7f50eb28 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -147,12 +147,16 @@ mediaPlayerSpeedFastDisabledIconOver: icon { }; mediaPlayerSpeedDisabledRippleBg: windowBgOver; -mediaPlayerPopupMenu: PopupMenu(defaultPopupMenu) { - menu: Menu(defaultMenu) { - itemIconPosition: point(6px, 5px); - itemPadding: margins(34px, 8px, 17px, 7px); +mediaPlayerMenu: DropdownMenu(defaultDropdownMenu) { + wrap: InnerDropdown(defaultInnerDropdown) { + scrollPadding: margins(0px, 8px, 0px, 8px); + padding: margins(10px, 2px, 10px, 10px); } } +mediaPlayerSpeedMenu: Menu(defaultMenu) { + itemIconPosition: point(6px, 5px); + itemPadding: margins(34px, 8px, 17px, 7px); +} mediaPlayerMenuCheck: icon {{ "player/player_check", mediaPlayerActiveFg }}; mediaPlayerVolumeIcon0: icon { @@ -287,7 +291,7 @@ mediaPlayerFileLayout: OverviewFileLayout(overviewFileLayout) { mediaPlayerFloatSize: 128px; mediaPlayerFloatMargin: 12px; -mediaPlayerOrderMenuPosition: point(-2px, -4px); +mediaPlayerMenuPosition: point(-2px, 0px); mediaPlayerOrderMenu: Menu(defaultMenu) { itemIconPosition: point(13px, 8px); itemPadding: margins(49px, 9px, 17px, 11px); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 07eb34157..33aed5f7c 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -41,47 +41,69 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Media { namespace Player { -using ButtonState = PlayButtonLayout::State; - -class Widget::PlayButton : public Ui::RippleButton { +class WithDropdownController { public: - PlayButton(QWidget *parent); + WithDropdownController( + not_null<Ui::IconButton*> button, + not_null<Ui::RpWidget*> menuParent, + Fn<void(bool)> menuOverCallback); + virtual ~WithDropdownController() = default; - void setState(PlayButtonLayout::State state) { - _layout.setState(state); - } - void finishTransform() { - _layout.finishTransform(); - } + [[nodiscard]] not_null<Ui::IconButton*> button() const; + Ui::DropdownMenu *menu() const; + + void updateDropdownGeometry(); + + void hideTemporarily(); + void showBack(); protected: - void paintEvent(QPaintEvent *e) override; - - QImage prepareRippleMask() const override; - QPoint prepareRippleStartPosition() const override; + void showMenu(); private: - PlayButtonLayout _layout; + virtual void fillMenu(not_null<Ui::DropdownMenu*> menu) = 0; + + const not_null<Ui::IconButton*> _button; + const not_null<Ui::RpWidget*> _menuParent; + const Fn<void(bool)> _menuOverCallback; + base::unique_qptr<Ui::DropdownMenu> _menu; + bool _temporarilyHidden = false; }; -class Widget::SpeedController final { +class Widget::OrderController final : public WithDropdownController { public: - explicit SpeedController(not_null<Ui::IconButton*> button); + OrderController( + not_null<Ui::IconButton*> button, + not_null<Ui::RpWidget*> menuParent, + Fn<void(bool)> menuOverCallback); + +private: + void fillMenu(not_null<Ui::DropdownMenu*> menu) override; + void updateIcon(); + +}; + +class Widget::SpeedController final : public WithDropdownController { +public: + SpeedController( + not_null<Ui::IconButton*> button, + not_null<Ui::RpWidget*> menuParent, + Fn<void(bool)> menuOverCallback); [[nodiscard]] rpl::producer<> saved() const; private: + void fillMenu(not_null<Ui::DropdownMenu*> menu) override; + void updateIcon(); + [[nodiscard]] float64 speed() const; [[nodiscard]] bool isDefault() const; [[nodiscard]] float64 lastNonDefaultSpeed() const; void toggleDefault(); void setSpeed(float64 newSpeed); void save(); - void showContextMenu(not_null<QContextMenuEvent*> e); - const not_null<Ui::IconButton*> _button; - base::unique_qptr<Ui::PopupMenu> _menu; float64 _speed = 2.; bool _isDefault = true; rpl::event_stream<float64> _speedChanged; @@ -89,70 +111,216 @@ private: }; -Widget::SpeedController::SpeedController(not_null<Ui::IconButton*> button) -: _button(button) { - setSpeed(Core::App().settings().voicePlaybackSpeed()); - _speed = Core::App().settings().voicePlaybackSpeed(true); +WithDropdownController::WithDropdownController( + not_null<Ui::IconButton*> button, + not_null<Ui::RpWidget*> menuParent, + Fn<void(bool)> menuOverCallback) +: _button(button) +, _menuParent(menuParent) +, _menuOverCallback(std::move(menuOverCallback)) { + button->events( + ) | rpl::filter([=](not_null<QEvent*> e) { + return (e->type() == QEvent::Enter); + }) | rpl::start_with_next([=] { + showMenu(); + }, button->lifetime()); +} +not_null<Ui::IconButton*> WithDropdownController::button() const { + return _button; +} + +Ui::DropdownMenu *WithDropdownController::menu() const { + return _menu.get(); +} + +void WithDropdownController::updateDropdownGeometry() { + if (!_menu) { + return; + } + const auto position = _menu->parentWidget()->mapFromGlobal( + _button->mapToGlobal( + QPoint(_button->width(), _button->height()))); + const auto padding = st::mediaPlayerMenu.wrap.padding; + _menu->move(position + - QPoint(_menu->width(), 0) + + QPoint(padding.right(), -padding.top()) + + st::mediaPlayerMenuPosition); +} + +void WithDropdownController::hideTemporarily() { + if (_menu && !_menu->isHidden()) { + _temporarilyHidden = true; + _menu->hide(); + } +} + +void WithDropdownController::showBack() { + if (_temporarilyHidden) { + _temporarilyHidden = false; + if (_menu && _menu->isHidden()) { + _menu->show(); + } + } +} + +void WithDropdownController::showMenu() { + if (_menu) { + return; + } + _menu.emplace(_menuParent, st::mediaPlayerMenu); + const auto raw = _menu.get(); + _menu->events( + ) | rpl::start_with_next([this](not_null<QEvent*> e) { + const auto type = e->type(); + if (type == QEvent::Enter) { + _menuOverCallback(true); + } else if (type == QEvent::Leave) { + _menuOverCallback(false); + } + }, _menu->lifetime()); + _menu->setHiddenCallback([=]{ + Ui::PostponeCall(raw, [this] { + _menu = nullptr; + }); + }); + _button->installEventFilter(raw); + fillMenu(raw); + updateDropdownGeometry(); + _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); +} + +Widget::OrderController::OrderController( + not_null<Ui::IconButton*> button, + not_null<Ui::RpWidget*> menuParent, + Fn<void(bool)> menuOverCallback) +: WithDropdownController(button, menuParent, std::move(menuOverCallback)) { + button->setClickedCallback([=] { + showMenu(); + }); + + Core::App().settings().playerOrderModeValue( + ) | rpl::start_with_next([=] { + updateIcon(); + }, button->lifetime()); +} + +void Widget::OrderController::fillMenu(not_null<Ui::DropdownMenu*> menu) { + const auto addOrderAction = [&](OrderMode mode) { + struct Fields { + QString label; + const style::icon &icon; + const style::icon &activeIcon; + }; + const auto current = Core::App().settings().playerOrderMode(); + const auto active = (current == mode); + const auto callback = [=] { + Core::App().settings().setPlayerOrderMode(active + ? OrderMode::Default + : mode); + Core::App().saveSettingsDelayed(); + }; + const auto fields = [&]() -> Fields { + switch (mode) { + case OrderMode::Reverse: return { + .label = tr::lng_audio_player_reverse(tr::now), + .icon = st::mediaPlayerOrderIconReverse, + .activeIcon = st::mediaPlayerOrderIconReverseActive, + }; + case OrderMode::Shuffle: return { + .label = tr::lng_audio_player_shuffle(tr::now), + .icon = st::mediaPlayerOrderIconShuffle, + .activeIcon = st::mediaPlayerOrderIconShuffleActive, + }; + } + Unexpected("Order mode in addOrderAction."); + }(); + menu->addAction(base::make_unique_q<Ui::Menu::Action>( + menu, + (active + ? st::mediaPlayerOrderMenuActive + : st::mediaPlayerOrderMenu), + Ui::Menu::CreateAction(menu, fields.label, callback), + &(active ? fields.activeIcon : fields.icon), + &(active ? fields.activeIcon : fields.icon))); + }; + addOrderAction(OrderMode::Reverse); + addOrderAction(OrderMode::Shuffle); +} + +void Widget::OrderController::updateIcon() { + switch (Core::App().settings().playerOrderMode()) { + case OrderMode::Default: + button()->setIconOverride( + &st::mediaPlayerReverseDisabledIcon, + &st::mediaPlayerReverseDisabledIconOver); + button()->setRippleColorOverride( + &st::mediaPlayerRepeatDisabledRippleBg); + break; + case OrderMode::Reverse: + button()->setIconOverride(&st::mediaPlayerReverseIcon); + button()->setRippleColorOverride(nullptr); + break; + case OrderMode::Shuffle: + button()->setIconOverride(&st::mediaPlayerShuffleIcon); + button()->setRippleColorOverride(nullptr); + break; + } +} + +Widget::SpeedController::SpeedController( + not_null<Ui::IconButton*> button, + not_null<Ui::RpWidget*> menuParent, + Fn<void(bool)> menuOverCallback) +: WithDropdownController(button, menuParent, std::move(menuOverCallback)) { button->setClickedCallback([=] { toggleDefault(); save(); + if (const auto current = menu()) { + current->otherEnter(); + } }); - struct Icons { - const style::icon *icon = nullptr; - const style::icon *over = nullptr; - }; + setSpeed(Core::App().settings().voicePlaybackSpeed()); + _speed = Core::App().settings().voicePlaybackSpeed(true); _speedChanged.events_starting_with( speed() - ) | rpl::start_with_next([=](float64 speed) { - const auto isDefaultSpeed = isDefault(); - const auto nonDefaultSpeed = lastNonDefaultSpeed(); - - const auto icons = [&]() -> Icons { - if (nonDefaultSpeed == .5) { - return { - .icon = isDefaultSpeed - ? &st::mediaPlayerSpeedSlowDisabledIcon - : &st::mediaPlayerSpeedSlowIcon, - .over = isDefaultSpeed - ? &st::mediaPlayerSpeedSlowDisabledIconOver - : &st::mediaPlayerSpeedSlowIcon, - }; - } else if (nonDefaultSpeed == 1.5) { - return { - .icon = isDefaultSpeed - ? &st::mediaPlayerSpeedFastDisabledIcon - : &st::mediaPlayerSpeedFastIcon, - .over = isDefaultSpeed - ? &st::mediaPlayerSpeedFastDisabledIconOver - : &st::mediaPlayerSpeedFastIcon, - }; - } else { - return { - .icon = isDefaultSpeed - ? &st::mediaPlayerSpeedDisabledIcon - : nullptr, // 2x icon. - .over = isDefaultSpeed - ? &st::mediaPlayerSpeedDisabledIconOver - : nullptr, // 2x icon. - }; - } - }(); - - button->setIconOverride(icons.icon, icons.over); - button->setRippleColorOverride(isDefaultSpeed - ? &st::mediaPlayerSpeedDisabledRippleBg - : nullptr); + ) | rpl::start_with_next([=] { + updateIcon(); }, button->lifetime()); +} - button->events( - ) | rpl::filter([=](not_null<QEvent*> e) { - return (e->type() == QEvent::ContextMenu); - }) | rpl::start_with_next([=](not_null<QEvent*> e) { - showContextMenu(static_cast<QContextMenuEvent*>(e.get())); - }, button->lifetime()); +void Widget::SpeedController::updateIcon() { + const auto isDefaultSpeed = isDefault(); + const auto nonDefaultSpeed = lastNonDefaultSpeed(); + + if (nonDefaultSpeed == .5) { + button()->setIconOverride( + (isDefaultSpeed + ? &st::mediaPlayerSpeedSlowDisabledIcon + : &st::mediaPlayerSpeedSlowIcon), + (isDefaultSpeed + ? &st::mediaPlayerSpeedSlowDisabledIconOver + : &st::mediaPlayerSpeedSlowIcon)); + } else if (nonDefaultSpeed == 1.5) { + button()->setIconOverride( + (isDefaultSpeed + ? &st::mediaPlayerSpeedFastDisabledIcon + : &st::mediaPlayerSpeedFastIcon), + (isDefaultSpeed + ? &st::mediaPlayerSpeedFastDisabledIconOver + : &st::mediaPlayerSpeedFastIcon)); + } else { + button()->setIconOverride( + isDefaultSpeed ? &st::mediaPlayerSpeedDisabledIcon : nullptr, + (isDefaultSpeed + ? &st::mediaPlayerSpeedDisabledIconOver + : nullptr)); + } + button()->setRippleColorOverride(isDefaultSpeed + ? &st::mediaPlayerSpeedDisabledRippleBg + : nullptr); } rpl::producer<> Widget::SpeedController::saved() const { @@ -189,56 +357,36 @@ void Widget::SpeedController::save() { _saved.fire({}); } -void Widget::SpeedController::showContextMenu( - not_null<QContextMenuEvent*> e) { - _menu = base::make_unique_q<Ui::PopupMenu>( - _button, - st::mediaPlayerPopupMenu); - - const auto setPlaybackSpeed = [=](float64 speed) { - setSpeed(speed); - save(); - }; - +void Widget::SpeedController::fillMenu(not_null<Ui::DropdownMenu*> menu) { const auto currentSpeed = speed(); - const auto addSpeed = [&](float64 speed, QString text = QString()) { - if (text.isEmpty()) { - text = QString::number(speed); - } - _menu->addAction( - text, - [=] { setPlaybackSpeed(speed); }, - (speed == currentSpeed) ? &st::mediaPlayerMenuCheck : nullptr); + const auto addSpeedAction = [&](float64 speed, QString text) { + const auto callback = [=] { + setSpeed(speed); + save(); + }; + const auto icon = (speed == currentSpeed) + ? &st::mediaPlayerMenuCheck + : nullptr; + auto action = base::make_unique_q<Ui::Menu::Action>( + menu, + st::mediaPlayerSpeedMenu, + Ui::Menu::CreateAction(menu, text, callback), + icon, + icon); + const auto raw = action.get(); + _speedChanged.events( + ) | rpl::start_with_next([=](float64 updatedSpeed) { + const auto icon = (speed == updatedSpeed) + ? &st::mediaPlayerMenuCheck + : nullptr; + raw->setIcon(icon, icon); + }, raw->lifetime()); + menu->addAction(std::move(action)); }; - addSpeed(0.5, tr::lng_voice_speed_slow(tr::now)); - addSpeed(1., tr::lng_voice_speed_normal(tr::now)); - addSpeed(1.5, tr::lng_voice_speed_fast(tr::now)); - addSpeed(2., tr::lng_voice_speed_very_fast(tr::now)); - - _menu->popup(e->globalPos()); -} - -Widget::PlayButton::PlayButton(QWidget *parent) : Ui::RippleButton(parent, st::mediaPlayerButton.ripple) -, _layout(st::mediaPlayerButton, [this] { update(); }) { - resize(st::mediaPlayerButtonSize); - setCursor(style::cur_pointer); -} - -void Widget::PlayButton::paintEvent(QPaintEvent *e) { - Painter p(this); - - paintRipple(p, st::mediaPlayerButton.rippleAreaPosition.x(), st::mediaPlayerButton.rippleAreaPosition.y()); - p.translate(st::mediaPlayerButtonPosition.x(), st::mediaPlayerButtonPosition.y()); - _layout.paint(p, st::mediaPlayerActiveFg); -} - -QImage Widget::PlayButton::prepareRippleMask() const { - auto size = QSize(st::mediaPlayerButton.rippleAreaSize, st::mediaPlayerButton.rippleAreaSize); - return Ui::RippleAnimation::ellipseMask(size); -} - -QPoint Widget::PlayButton::prepareRippleStartPosition() const { - return QPoint(mapFromGlobal(QCursor::pos()) - st::mediaPlayerButton.rippleAreaPosition); + addSpeedAction(0.5, tr::lng_voice_speed_slow(tr::now)); + addSpeedAction(1., tr::lng_voice_speed_normal(tr::now)); + addSpeedAction(1.5, tr::lng_voice_speed_fast(tr::now)); + addSpeedAction(2., tr::lng_voice_speed_very_fast(tr::now)); } Widget::Widget( @@ -255,15 +403,22 @@ Widget::Widget( , _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle) , _repeatToggle(rightControls(), st::mediaPlayerRepeatButton) , _orderToggle(rightControls(), st::mediaPlayerRepeatButton) -, _playbackSpeed(rightControls(), st::mediaPlayerSpeedButton) +, _speedToggle(rightControls(), st::mediaPlayerSpeedButton) , _close(this, st::mediaPlayerClose) , _shadow(this) , _playbackSlider(this, st::mediaPlayerPlayback) , _volume(std::in_place, dropdownsParent.get()) , _playbackProgress(std::make_unique<View::PlaybackProgress>()) +, _orderController( + std::make_unique<OrderController>( + _orderToggle.data(), + dropdownsParent, + [=](bool over) { markOver(over); })) , _speedController( std::make_unique<SpeedController>( - _playbackSpeed.data())) { + _speedToggle.data(), + dropdownsParent, + [=](bool over) { markOver(over); })) { setAttribute(Qt::WA_OpaquePaintEvent); setMouseTracking(true); resize(width(), st::mediaPlayerHeight + st::lineWidth); @@ -316,21 +471,6 @@ Widget::Widget( updateRepeatToggleIcon(); }, lifetime()); - _orderToggle->setClickedCallback([=] { - showOrderMenu(); - }); - _orderToggle->events( - ) | rpl::filter([=](not_null<QEvent*> e) { - return e->type() == QEvent::Enter; - }) | rpl::start_with_next([=] { - showOrderMenu(); - }, _orderToggle->lifetime()); - - Core::App().settings().playerOrderModeValue( - ) | rpl::start_with_next([=] { - updateOrderToggleIcon(); - }, lifetime()); - _repeatToggle->setClickedCallback([=] { auto &settings = Core::App().settings(); settings.setPlayerRepeatMode([&] { @@ -377,7 +517,14 @@ Widget::Widget( PrepareVolumeDropdown(_volume.get(), controller); _volumeToggle->installEventFilter(_volume.get()); - _volume->installEventFilter(this); + _volume->events( + ) | rpl::start_with_next([=](not_null<QEvent*> e) { + if (e->type() == QEvent::Enter) { + markOver(true); + } else if (e->type() == QEvent::Leave) { + markOver(false); + } + }, _volume->lifetime()); hidePlaylistOn(_playPause); hidePlaylistOn(_close); @@ -460,80 +607,8 @@ void Widget::showShadowAndDropdowns() { _volumeHidden = false; _volume->show(); } -} - -void Widget::showOrderMenu() { - if (_orderMenu) { - return; - } - _orderMenu.emplace(_orderMenuParent); - _orderMenu->installEventFilter(this); - _orderMenu->setHiddenCallback([weak = Ui::MakeWeak(this), menu = _orderMenu.get()] { - menu->deleteLater(); - if (weak && weak->_orderMenu == menu) { - weak->_orderMenu = nullptr; - weak->_orderToggle->setForceRippled(false); - } - }); - _orderMenu->setShowStartCallback(crl::guard(this, [this, menu = _orderMenu.get()] { - if (_orderMenu == menu) { - _orderToggle->setForceRippled(true); - } - })); - _orderMenu->setHideStartCallback(crl::guard(this, [this, menu = _orderMenu.get()] { - if (_orderMenu == menu) { - _orderToggle->setForceRippled(false); - } - })); - _orderToggle->installEventFilter(_orderMenu); - const auto addOrderAction = [&](OrderMode mode) { - struct Fields { - QString label; - const style::icon &icon; - const style::icon &activeIcon; - }; - const auto current = Core::App().settings().playerOrderMode(); - const auto active = (current == mode); - const auto callback = [=] { - Core::App().settings().setPlayerOrderMode(active - ? OrderMode::Default - : mode); - Core::App().saveSettingsDelayed(); - }; - const auto fields = [&]() -> Fields { - switch (mode) { - case OrderMode::Reverse: return { - .label = tr::lng_audio_player_reverse(tr::now), - .icon = st::mediaPlayerOrderIconReverse, - .activeIcon = st::mediaPlayerOrderIconReverseActive, - }; - case OrderMode::Shuffle: return { - .label = tr::lng_audio_player_shuffle(tr::now), - .icon = st::mediaPlayerOrderIconShuffle, - .activeIcon = st::mediaPlayerOrderIconShuffleActive, - }; - } - Unexpected("Order mode in addOrderAction."); - }(); - const auto parent = _orderMenu.get(); - const auto action = Ui::Menu::CreateAction( - parent, - fields.label, - callback); - auto item = base::make_unique_q<Ui::Menu::Action>( - parent, - (active - ? st::mediaPlayerOrderMenuActive - : st::mediaPlayerOrderMenu), - std::move(action), - &(active ? fields.activeIcon : fields.icon), - &(active ? fields.activeIcon : fields.icon)); - _orderMenu->addAction(std::move(item)); - }; - addOrderAction(OrderMode::Reverse); - addOrderAction(OrderMode::Shuffle); - updateDropdownsGeometry(); - _orderMenu->showAnimated(Ui::PanelAnimation::Origin::TopRight); + _speedController->showBack(); + _orderController->showBack(); } void Widget::updateDropdownsGeometry() { @@ -547,16 +622,8 @@ void Widget::updateDropdownsGeometry() { const auto shift = QPoint(playerMargins.left(), playerMargins.top()); _volume->move(position - shift); - if (_orderMenu) { - const auto position = _orderMenu->parentWidget()->mapFromGlobal( - _orderToggle->mapToGlobal( - QPoint(_orderToggle->width(), _orderToggle->height()))); - const auto padding = st::defaultInnerDropdown.padding; - _orderMenu->move(position - - QPoint(_orderMenu->width(), 0) - + QPoint(padding.right(), -padding.top()) - + st::mediaPlayerOrderMenuPosition); - } + _orderController->updateDropdownGeometry(); + _speedController->updateDropdownGeometry(); } void Widget::hideShadowAndDropdowns() { @@ -566,6 +633,8 @@ void Widget::hideShadowAndDropdowns() { _volumeHidden = true; _volume->hide(); } + _speedController->hideTemporarily(); + _orderController->hideTemporarily(); } void Widget::raiseDropdowns() { @@ -611,7 +680,7 @@ void Widget::updateControlsGeometry() { _close->moveToRight(st::mediaPlayerCloseRight, st::mediaPlayerPlayTop); auto right = 0; if (hasPlaybackSpeedControl()) { - _playbackSpeed->moveToRight(right, 0); right += _playbackSpeed->width(); + _speedToggle->moveToRight(right, 0); right += _speedToggle->width(); } _repeatToggle->moveToRight(right, 0); right += _repeatToggle->width(); _orderToggle->moveToRight(right, 0); right += _orderToggle->width(); @@ -621,7 +690,11 @@ void Widget::updateControlsGeometry() { updatePlayPrevNextPositions(); - _playbackSlider->setGeometry(0, height() - st::mediaPlayerPlayback.fullWidth, width(), st::mediaPlayerPlayback.fullWidth); + _playbackSlider->setGeometry( + 0, + height() - st::mediaPlayerPlayback.fullWidth, + width(), + st::mediaPlayerPlayback.fullWidth); updateDropdownsGeometry(); } @@ -649,16 +722,6 @@ void Widget::paintEvent(QPaintEvent *e) { } } -bool Widget::eventFilter(QObject *o, QEvent *e) { - const auto type = e->type(); - if (type == QEvent::Enter) { - markOver(true); - } else if (type == QEvent::Leave) { - markOver(false); - } - return RpWidget::eventFilter(o, e); -} - void Widget::enterEventHook(QEnterEvent *e) { markOver(true); } @@ -767,7 +830,7 @@ int Widget::getTimeRight() const { + _volumeToggle->width(); } if (hasPlaybackSpeedControl()) { - result += _playbackSpeed->width(); + result += _speedToggle->width(); } result += st::mediaPlayerPadding; return result; @@ -807,26 +870,6 @@ void Widget::updateRepeatToggleIcon() { } } -void Widget::updateOrderToggleIcon() { - switch (Core::App().settings().playerOrderMode()) { - case OrderMode::Default: - _orderToggle->setIconOverride( - &st::mediaPlayerReverseDisabledIcon, - &st::mediaPlayerReverseDisabledIconOver); - _orderToggle->setRippleColorOverride( - &st::mediaPlayerRepeatDisabledRippleBg); - break; - case OrderMode::Reverse: - _orderToggle->setIconOverride(&st::mediaPlayerReverseIcon); - _orderToggle->setRippleColorOverride(nullptr); - break; - case OrderMode::Shuffle: - _orderToggle->setIconOverride(&st::mediaPlayerShuffleIcon); - _orderToggle->setRippleColorOverride(nullptr); - break; - } -} - void Widget::checkForTypeChange() { auto hasActiveType = [](AudioMsgId::Type type) { const auto current = instance()->current(type); @@ -850,7 +893,7 @@ void Widget::updateControlsVisibility() { _repeatToggle->setVisible(_type == AudioMsgId::Type::Song); _orderToggle->setVisible(_type == AudioMsgId::Type::Song); _volumeToggle->setVisible(_type == AudioMsgId::Type::Song); - _playbackSpeed->setVisible(hasPlaybackSpeedControl()); + _speedToggle->setVisible(hasPlaybackSpeedControl()); if (!_shadow->isHidden()) { _playbackSlider->setVisible(_type == AudioMsgId::Type::Song); } diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index 57346b57d..ebe6a367e 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -18,7 +18,6 @@ class FlatLabel; class LabelSimple; class IconButton; class PlainShadow; -class DropdownMenu; class FilledSlider; template <typename Widget> class FadeWrap; @@ -67,7 +66,6 @@ public: private: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; - bool eventFilter(QObject *o, QEvent *e) override; void enterEventHook(QEnterEvent *e) override; void leaveEventHook(QEvent *e) override; @@ -91,7 +89,6 @@ private: void updatePlayPrevNextPositions(); void updateLabelsGeometry(); void updateRepeatToggleIcon(); - void updateOrderToggleIcon(); void updateControlsVisibility(); void updateControlsGeometry(); void updateControlsWrapGeometry(); @@ -112,8 +109,6 @@ private: void updateTimeLabel(); void markOver(bool over); - void showOrderMenu(); - const not_null<Window::SessionController*> _controller; const not_null<Ui::RpWidget*> _orderMenuParent; @@ -140,6 +135,7 @@ private: bool _volumeHidden = false; class PlayButton; + class OrderController; class SpeedController; object_ptr<Ui::FlatLabel> _nameLabel; object_ptr<Ui::FadeWrap<Ui::RpWidget>> _rightControls; @@ -150,13 +146,13 @@ private: object_ptr<Ui::IconButton> _volumeToggle; object_ptr<Ui::IconButton> _repeatToggle; object_ptr<Ui::IconButton> _orderToggle; - object_ptr<Ui::IconButton> _playbackSpeed; + object_ptr<Ui::IconButton> _speedToggle; object_ptr<Ui::IconButton> _close; object_ptr<Ui::PlainShadow> _shadow = { nullptr }; object_ptr<Ui::FilledSlider> _playbackSlider; base::unique_qptr<Dropdown> _volume; - base::unique_qptr<Ui::DropdownMenu> _orderMenu; std::unique_ptr<View::PlaybackProgress> _playbackProgress; + std::unique_ptr<OrderController> _orderController; std::unique_ptr<SpeedController> _speedController; rpl::lifetime _playlistChangesLifetime; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index f4a32c34f..725654654 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit f4a32c34f830b498a7348a94f171c7557a8252c3 +Subproject commit 725654654d44554c7ce2ebd86d242f7c46b13591 From 886ff7de50265355dbd4b82a8abd10d2d91d0bb4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 17:30:40 +0400 Subject: [PATCH 099/180] Fix different colors in active emoji icon. --- Telegram/SourceFiles/ui/chat/chat.style | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 795da8759..8daef9a37 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -308,7 +308,7 @@ historyMessagesTTL2Icon: icon {{ "chat/input_autodelete_7d", historyComposeIconF historyMessagesTTL2IconOver: icon {{ "chat/input_autodelete_7d", historyComposeIconFgOver }}; historyMessagesTTL3Icon: icon {{ "chat/input_autodelete_30d", historyComposeIconFg }}; historyMessagesTTL3IconOver: icon {{ "chat/input_autodelete_30d", historyComposeIconFgOver }}; -historyAttachEmojiFgActive: windowActiveTextFg; +historyAttachEmojiFgActive: windowBgActive; historyAttachEmojiActive: icon {{ "chat/input_smile_face", historyAttachEmojiFgActive }}; historyAttachEmojiTooltipDelta: 4px; historyEmojiCircle: size(20px, 20px); From 0f443da75896cf7ddd7bff8aee75654593052434 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 18:33:53 +0400 Subject: [PATCH 100/180] Track noforwards flag in specific messages. --- Telegram/SourceFiles/data/data_document.cpp | 2 +- Telegram/SourceFiles/data/data_peer.cpp | 3 +- Telegram/SourceFiles/data/data_types.h | 31 +++--- .../history/history_inner_widget.cpp | 94 ++++++++++++------- .../history/history_inner_widget.h | 8 +- Telegram/SourceFiles/history/history_item.cpp | 7 +- Telegram/SourceFiles/history/history_item.h | 1 + .../SourceFiles/history/history_message.cpp | 1 + .../view/history_view_context_menu.cpp | 42 +++++---- .../history/view/history_view_list_widget.cpp | 45 +++++++-- .../history/view/history_view_list_widget.h | 15 ++- .../history/view/history_view_message.cpp | 2 +- .../view/history_view_pinned_section.cpp | 5 +- .../view/history_view_pinned_section.h | 2 +- .../view/history_view_replies_section.cpp | 5 +- .../view/history_view_replies_section.h | 2 +- .../view/history_view_scheduled_section.cpp | 3 +- .../view/history_view_scheduled_section.h | 2 +- .../view/media/history_view_document.cpp | 5 +- .../history/view/media/history_view_gif.cpp | 5 +- .../media/view/media_view_overlay_widget.cpp | 3 +- .../SourceFiles/overview/overview_layout.cpp | 5 +- 22 files changed, 179 insertions(+), 109 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index c7849e800..d29c99828 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -1165,7 +1165,7 @@ bool DocumentData::canBeStreamed(HistoryItem *item) const { && supportsStreaming() && (!isVideoFile() || !cUseExternalVideoPlayer() - || (item && !item->history()->peer->allowsForwarding())); + || (item && !item->allowsForward())); } void DocumentData::setInappPlaybackFailed() { diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index a99105659..c8abcbb6e 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -521,8 +521,7 @@ bool PeerData::canEditMessagesIndefinitely() const { bool PeerData::canExportChatHistory() const { if (isRepliesChat() || !allowsForwarding()) { return false; - } - if (const auto channel = asChannel()) { + } else if (const auto channel = asChannel()) { if (!channel->amIn() && channel->invitePeekExpires()) { return false; } diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index d1bdb5b8e..1bb5f0db6 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -236,52 +236,53 @@ enum class MessageFlag : uint32 { MediaIsUnread = (1U << 13), MentionsMe = (1U << 14), IsOrWasScheduled = (1U << 15), + NoForwards = (1U << 16), // Needs to return back to inline mode. - HasSwitchInlineButton = (1U << 16), + HasSwitchInlineButton = (1U << 17), // For "shared links" indexing. - HasTextLinks = (1U << 17), + HasTextLinks = (1U << 18), // Group / channel create or migrate service message. - IsGroupEssential = (1U << 18), + IsGroupEssential = (1U << 19), // Edited media is generated on the client // and should not update media from server. - IsLocalUpdateMedia = (1U << 19), + IsLocalUpdateMedia = (1U << 20), // Sent from inline bot, need to re-set media when sent. - FromInlineBot = (1U << 20), + FromInlineBot = (1U << 21), // Generated on the client side and should be unread. - ClientSideUnread = (1U << 21), + ClientSideUnread = (1U << 22), // In a supergroup. - HasAdminBadge = (1U << 22), + HasAdminBadge = (1U << 23), // Outgoing message that is being sent. - BeingSent = (1U << 23), + BeingSent = (1U << 24), // Outgoing message and failed to be sent. - SendingFailed = (1U << 24), + SendingFailed = (1U << 25), // No media and only a several emoji text. - IsolatedEmoji = (1U << 25), + IsolatedEmoji = (1U << 26), // Message existing in the message history. - HistoryEntry = (1U << 26), + HistoryEntry = (1U << 27), // Local message, not existing on the server. - Local = (1U << 27), + Local = (1U << 28), // Fake message for some UI element. - FakeHistoryItem = (1U << 28), + FakeHistoryItem = (1U << 29), // Contact sign-up message, notification should be skipped for Silent. - IsContactSignUp = (1U << 29), + IsContactSignUp = (1U << 30), // In channels. - IsSponsored = (1U << 30), + IsSponsored = (1U << 31), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags<MessageFlag>; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index f61bd2374..96e3607f7 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1546,7 +1546,7 @@ void HistoryInner::mouseActionFinish( if (QGuiApplication::clipboard()->supportsSelection() && !_selected.empty() && _selected.cbegin()->second != FullSelection - && !hasCopyRestriction()) { + && !hasCopyRestriction(_selected.cbegin()->first)) { const auto [item, selection] = *_selected.cbegin(); if (const auto view = item->mainView()) { TextUtilities::SetClipboardText( @@ -1723,14 +1723,15 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { })); } }; - const auto addPhotoActions = [&](not_null<PhotoData*> photo) { + const auto addPhotoActions = [&](not_null<PhotoData*> photo, HistoryItem *item) { const auto media = photo->activeMediaView(); - if (!photo->isNull() && media && media->loaded() && !hasCopyRestriction()) { + const auto itemId = item ? item->fullId() : FullMsgId(); + if (!photo->isNull() && media && media->loaded() && !hasCopyRestriction(item)) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { savePhotoToFile(photo); })); _menu->addAction(tr::lng_context_copy_image(tr::now), [=] { - copyContextImage(photo); + copyContextImage(photo, itemId); }); } if (photo->hasAttachedStickers()) { @@ -1741,14 +1742,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }); } }; - const auto addDocumentActions = [&](not_null<DocumentData*> document) { + const auto addDocumentActions = [&](not_null<DocumentData*> document, HistoryItem *item) { if (document->loading()) { _menu->addAction(tr::lng_context_cancel_download(tr::now), [=] { cancelContextDownload(document); }); return; } - const auto item = _dragStateItem; const auto itemId = item ? item->fullId() : FullMsgId(); const auto lnkIsVideo = document->isVideoFile(); const auto lnkIsVoice = document->isVoiceMessage(); @@ -1767,7 +1767,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { openContextGif(itemId); }); } - if (!hasCopyRestriction()) { + if (!hasCopyRestriction(item)) { _menu->addAction(tr::lng_context_save_gif(tr::now), [=] { saveContextGif(itemId); }); @@ -1778,7 +1778,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { showContextInFolder(document); }); } - if (!hasCopyRestriction()) { + if (!hasCopyRestriction(item)) { _menu->addAction(lnkIsVideo ? tr::lng_context_save_video(tr::now) : (lnkIsVoice ? tr::lng_context_save_audio(tr::now) : (lnkIsAudio ? tr::lng_context_save_audio_file(tr::now) : tr::lng_context_save_file(tr::now))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { saveDocumentToFile(itemId, document); })); @@ -1832,7 +1832,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (lnkPhoto || lnkDocument) { const auto item = _dragStateItem; const auto itemId = item ? item->fullId() : FullMsgId(); - if (isUponSelected > 0 && !hasCopyRestriction()) { + if (isUponSelected > 0 && !hasCopyRestrictionForSelected()) { _menu->addAction( (isUponSelected > 1 ? tr::lng_context_copy_selected_items(tr::now) @@ -1841,9 +1841,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } addItemActions(item, item); if (lnkPhoto) { - addPhotoActions(lnkPhoto->photo()); + addPhotoActions(lnkPhoto->photo(), item); } else { - addDocumentActions(lnkDocument->document()); + addDocumentActions(lnkDocument->document(), item); } if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) { _menu->addAction(item->history()->peer->isMegagroup() ? tr::lng_context_copy_message_link(tr::now) : tr::lng_context_copy_post_link(tr::now), [=] { @@ -1913,7 +1913,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto view = item ? item->mainView() : nullptr; if (isUponSelected > 0) { - if (!hasCopyRestriction()) { + if (!hasCopyRestrictionForSelected()) { _menu->addAction( ((isUponSelected > 1) ? tr::lng_context_copy_selected_items(tr::now) @@ -1936,7 +1936,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { Api::ToggleFavedSticker(document, itemId); }); } - if (!hasCopyRestriction()) { + if (!hasCopyRestriction(item)) { _menu->addAction(tr::lng_context_save_image(tr::now), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] { saveDocumentToFile(itemId, document); })); @@ -1965,7 +1965,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (!item->isService() && view && !link - && !hasCopyRestriction() + && !hasCopyRestriction(item) && (view->hasVisibleText() || mediaHasTextForCopy)) { _menu->addAction(tr::lng_context_copy_text(tr::now), [=] { copyContextText(itemId); @@ -2048,12 +2048,12 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } -bool HistoryInner::hasCopyRestriction() const { - return !_peer->allowsForwarding(); +bool HistoryInner::hasCopyRestriction(HistoryItem *item) const { + return !_peer->allowsForwarding() || (item && item->forbidsForward()); } -bool HistoryInner::showCopyRestriction() { - if (!hasCopyRestriction()) { +bool HistoryInner::showCopyRestriction(HistoryItem *item) { + if (!hasCopyRestriction(item)) { return false; } Ui::ShowMultilineToast({ @@ -2064,8 +2064,29 @@ bool HistoryInner::showCopyRestriction() { return true; } +bool HistoryInner::hasCopyRestrictionForSelected() const { + if (hasCopyRestriction()) { + return true; + } + for (const auto &[item, selection] : _selected) { + if (item && item->forbidsForward()) { + return true; + } + } + return false; +} + +bool HistoryInner::showCopyRestrictionForSelected() { + for (const auto &[item, selection] : _selected) { + if (showCopyRestriction(item)) { + return true; + } + } + return false; +} + void HistoryInner::copySelectedText() { - if (!showCopyRestriction()) { + if (!showCopyRestrictionForSelected()) { TextUtilities::SetClipboardText(getSelectedText()); } } @@ -2092,11 +2113,14 @@ void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) { })); } -void HistoryInner::copyContextImage(not_null<PhotoData*> photo) { +void HistoryInner::copyContextImage( + not_null<PhotoData*> photo, + FullMsgId itemId) { + const auto item = session().data().message(itemId); const auto media = photo->activeMediaView(); if (photo->isNull() || !media || !media->loaded()) { return; - } else if (!showCopyRestriction()) { + } else if (!showCopyRestriction(item)) { const auto image = media->image(Data::PhotoSize::Large)->original(); QGuiApplication::clipboard()->setImage(image); } @@ -2137,25 +2161,25 @@ void HistoryInner::openContextGif(FullMsgId itemId) { } void HistoryInner::saveContextGif(FullMsgId itemId) { - if (hasCopyRestriction()) { - return; - } else if (const auto item = session().data().message(itemId)) { - if (const auto media = item->media()) { - if (const auto document = media->document()) { - Api::ToggleSavedGif(document, item->fullId(), true); + if (const auto item = session().data().message(itemId)) { + if (!hasCopyRestriction(item)) { + if (const auto media = item->media()) { + if (const auto document = media->document()) { + Api::ToggleSavedGif(document, item->fullId(), true); + } } } } } void HistoryInner::copyContextText(FullMsgId itemId) { - if (showCopyRestriction()) { - return; - } else if (const auto item = session().data().message(itemId)) { - if (const auto group = session().data().groups().find(item)) { - TextUtilities::SetClipboardText(HistoryGroupText(group)); - } else { - TextUtilities::SetClipboardText(HistoryItemText(item)); + if (const auto item = session().data().message(itemId)) { + if (!showCopyRestriction(item)) { + if (const auto group = session().data().groups().find(item)) { + TextUtilities::SetClipboardText(HistoryGroupText(group)); + } else { + TextUtilities::SetClipboardText(HistoryItemText(item)); + } } } } @@ -2248,7 +2272,7 @@ void HistoryInner::keyPressEvent(QKeyEvent *e) { #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier) - && !showCopyRestriction()) { + && !showCopyRestrictionForSelected()) { TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index c7e28ca60..8e0302223 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -265,7 +265,7 @@ private: void saveDocumentToFile( FullMsgId contextId, not_null<DocumentData*> document); - void copyContextImage(not_null<PhotoData*> photo); + void copyContextImage(not_null<PhotoData*> photo, FullMsgId itemId); void showStickerPackInfo(not_null<DocumentData*> document); void itemRemoved(not_null<const HistoryItem*> item); @@ -343,8 +343,10 @@ private: void copySelectedText(); void setupSharingDisallowed(); - [[nodiscard]] bool hasCopyRestriction() const; - bool showCopyRestriction(); + [[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const; + bool showCopyRestriction(HistoryItem *item = nullptr); + [[nodiscard]] bool hasCopyRestrictionForSelected() const; + bool showCopyRestrictionForSelected(); [[nodiscard]] bool hasSelectRestriction() const; // Does any of the shown histories has this flag set. diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 386efa430..93ab1cd46 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -645,6 +645,10 @@ bool HistoryItem::canStopPoll() const { return canBeEdited() && isRegular(); } +bool HistoryItem::forbidsForward() const { + return (_flags & MessageFlag::NoForwards); +} + bool HistoryItem::canDelete() const { if (isSponsored()) { return false; @@ -1088,7 +1092,8 @@ MessageFlags FlagsFromMTP( | ((flags & MTP::f_reply_to) ? Flag::HasReplyInfo : Flag()) | ((flags & MTP::f_reply_markup) ? Flag::HasReplyMarkup : Flag()) | ((flags & MTP::f_from_scheduled) ? Flag::IsOrWasScheduled : Flag()) - | ((flags & MTP::f_views) ? Flag::HasViews : Flag()); + | ((flags & MTP::f_views) ? Flag::HasViews : Flag()) + | ((flags & MTP::f_noforwards) ? Flag::NoForwards : Flag()); } MessageFlags FlagsFromMTP( diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 585be3048..7ff64716b 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -375,6 +375,7 @@ public: [[nodiscard]] bool canPin() const; [[nodiscard]] bool canBeEdited() const; [[nodiscard]] bool canStopPoll() const; + [[nodiscard]] bool forbidsForward() const; [[nodiscard]] virtual bool allowsSendNow() const; [[nodiscard]] virtual bool allowsForward() const; [[nodiscard]] virtual bool allowsEdit(TimeId now) const; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 2fccf51b7..601133d0b 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1039,6 +1039,7 @@ void HistoryMessage::applySentMessage( bool HistoryMessage::allowsForward() const { return isRegular() + && !forbidsForward() && history()->peer->allowsForwarding() && (!_media || _media->allowsForward()); } diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 3b0b9bb27..a4767536d 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -128,8 +128,10 @@ void ToggleFavedSticker( void AddPhotoActions( not_null<Ui::PopupMenu*> menu, not_null<PhotoData*> photo, + HistoryItem *item, not_null<ListWidget*> list) { - if (!list->hasCopyRestriction()) { + const auto contextId = item ? item->fullId() : FullMsgId(); + if (!list->hasCopyRestriction(item)) { menu->addAction( tr::lng_context_save_image(tr::now), App::LambdaDelayed( @@ -137,7 +139,8 @@ void AddPhotoActions( &photo->session(), [=] { SavePhotoToFile(photo); })); menu->addAction(tr::lng_context_copy_image(tr::now), [=] { - if (!list->showCopyRestriction()) { + const auto item = photo->owner().message(contextId); + if (!list->showCopyRestriction(item)) { CopyImage(photo); } }); @@ -187,12 +190,14 @@ void ShowInFolder(not_null<DocumentData*> document) { void AddSaveDocumentAction( not_null<Ui::PopupMenu*> menu, - Data::FileOrigin origin, + HistoryItem *item, not_null<DocumentData*> document, not_null<ListWidget*> list) { - if (list->hasCopyRestriction()) { + if (list->hasCopyRestriction(item)) { return; } + const auto origin = Data::FileOrigin( + item ? item->fullId() : FullMsgId()); const auto save = [=] { DocumentSaveClickHandler::Save( origin, @@ -219,7 +224,7 @@ void AddSaveDocumentAction( void AddDocumentActions( not_null<Ui::PopupMenu*> menu, not_null<DocumentData*> document, - FullMsgId contextId, + HistoryItem *item, not_null<ListWidget*> list) { if (document->loading()) { menu->addAction(tr::lng_context_cancel_download(tr::now), [=] { @@ -227,8 +232,9 @@ void AddDocumentActions( }); return; } + const auto contextId = item ? item->fullId() : FullMsgId(); const auto session = &document->session(); - if (const auto item = session->data().message(contextId)) { + if (item) { const auto notAutoplayedGif = [&] { return document->isGifv() && !Data::AutoDownload::ShouldAutoPlay( @@ -241,7 +247,7 @@ void AddDocumentActions( OpenGif(list->controller(), contextId); }); } - if (document->isGifv() && !list->hasCopyRestriction()) { + if (document->isGifv() && !list->hasCopyRestriction(item)) { menu->addAction(tr::lng_context_save_gif(tr::now), [=] { SaveGif(list->controller(), contextId); }); @@ -276,7 +282,7 @@ void AddDocumentActions( tr::lng_context_attached_stickers(tr::now), std::move(callback)); } - AddSaveDocumentAction(menu, contextId, document, list); + AddSaveDocumentAction(menu, item, document, list); } void AddPostLinkAction( @@ -911,12 +917,12 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu( const auto hasSelection = !request.selectedItems.empty() || !request.selectedText.empty(); - if (request.overSelection && !list->hasCopyRestriction()) { + if (request.overSelection && !list->hasCopyRestrictionForSelected()) { const auto text = request.selectedItems.empty() ? tr::lng_context_copy_selected(tr::now) : tr::lng_context_copy_selected_items(tr::now); result->addAction(text, [=] { - if (!list->showCopyRestriction()) { + if (!list->showCopyRestrictionForSelected()) { TextUtilities::SetClipboardText(list->getSelectedText()); } }); @@ -924,9 +930,9 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu( AddTopMessageActions(result, request, list); if (linkPhoto) { - AddPhotoActions(result, photo, list); + AddPhotoActions(result, photo, item, list); } else if (linkDocument) { - AddDocumentActions(result, document, itemId, list); + AddDocumentActions(result, document, item, list); } else if (poll) { AddPollActions(result, poll, item, list->elementContext()); } else if (!request.overSelection && view && !hasSelection) { @@ -934,19 +940,15 @@ base::unique_qptr<Ui::PopupMenu> FillContextMenu( const auto media = view->media(); const auto mediaHasTextForCopy = media && media->hasTextForCopy(); if (const auto document = media ? media->getDocument() : nullptr) { - AddDocumentActions( - result, - document, - view->data()->fullId(), - list); + AddDocumentActions(result, document, view->data(), list); } if (!link && (view->hasVisibleText() || mediaHasTextForCopy) - && !list->hasCopyRestriction()) { + && !list->hasCopyRestriction(view->data())) { const auto asGroup = (request.pointState != PointState::GroupPart); result->addAction(tr::lng_context_copy_text(tr::now), [=] { - if (!list->showCopyRestriction()) { - if (const auto item = owner->message(itemId)) { + if (const auto item = owner->message(itemId)) { + if (!list->showCopyRestriction(item)) { if (asGroup) { if (const auto group = owner->groups().find(item)) { TextUtilities::SetClipboardText(HistoryGroupText(group)); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index e7f3c1757..e0316f99f 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1169,12 +1169,13 @@ bool ListWidget::isEmpty() const { && (_itemsHeight + _itemsRevealHeight == 0); } -bool ListWidget::hasCopyRestriction() const { - return _delegate->listCopyRestrictionType() != CopyRestrictionType::None; +bool ListWidget::hasCopyRestriction(HistoryItem *item) const { + return _delegate->listCopyRestrictionType(item) + != CopyRestrictionType::None; } -bool ListWidget::showCopyRestriction() { - const auto type = _delegate->listCopyRestrictionType(); +bool ListWidget::showCopyRestriction(HistoryItem *item) { + const auto type = _delegate->listCopyRestrictionType(item); if (type == CopyRestrictionType::None) { return false; } @@ -1186,6 +1187,29 @@ bool ListWidget::showCopyRestriction() { return true; } +bool ListWidget::hasCopyRestrictionForSelected() const { + if (hasCopyRestriction()) { + return true; + } + for (const auto [itemId, selection] : _selected) { + if (const auto item = session().data().message(itemId)) { + if (item->forbidsForward()) { + return true; + } + } + } + return false; +} + +bool ListWidget::showCopyRestrictionForSelected() { + for (const auto [itemId, selection] : _selected) { + if (showCopyRestriction(session().data().message(itemId))) { + return true; + } + } + return false; +} + bool ListWidget::hasSelectRestriction() const { return _delegate->listSelectRestrictionType() != CopyRestrictionType::None; @@ -1922,12 +1946,14 @@ void ListWidget::keyPressEvent(QKeyEvent *e) { } } else if (e == QKeySequence::Copy && (hasSelectedText() || hasSelectedItems()) - && !showCopyRestriction()) { + && !showCopyRestriction() + && !hasCopyRestrictionForSelected()) { TextUtilities::SetClipboardText(getSelectedText()); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier) - && !showCopyRestriction()) { + && !showCopyRestriction() + && !hasCopyRestrictionForSelected()) { TextUtilities::SetClipboardText(getSelectedText(), QClipboard::FindBuffer); #endif // Q_OS_MAC } else if (e == QKeySequence::Delete) { @@ -2451,7 +2477,7 @@ void ListWidget::mouseActionFinish( if (QGuiApplication::clipboard()->supportsSelection() && _selectedTextItem && _selectedTextRange.from != _selectedTextRange.to - && !hasCopyRestriction()) { + && !hasCopyRestriction(_selectedTextItem)) { if (const auto view = viewForItem(_selectedTextItem)) { TextUtilities::SetClipboardText( view->selectedText(_selectedTextRange), @@ -3091,8 +3117,9 @@ void ConfirmSendNowSelectedItems(not_null<ListWidget*> widget) { } CopyRestrictionType CopyRestrictionTypeFor( - not_null<PeerData*> peer) { - return peer->allowsForwarding() + not_null<PeerData*> peer, + HistoryItem *item) { + return (peer->allowsForwarding() && (!item || !item->forbidsForward())) ? CopyRestrictionType::None : peer->isBroadcast() ? CopyRestrictionType::Channel diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 2ced1f8b9..f12a1436c 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -100,7 +100,11 @@ public: const FullMsgId &context) = 0; virtual void listHandleViaClick(not_null<UserData*> bot) = 0; virtual not_null<Ui::ChatTheme*> listChatTheme() = 0; - virtual CopyRestrictionType listCopyRestrictionType() = 0; + virtual CopyRestrictionType listCopyRestrictionType( + HistoryItem *item) = 0; + CopyRestrictionType listCopyRestrictionType() { + return listCopyRestrictionType(nullptr); + } virtual CopyRestrictionType listSelectRestrictionType() = 0; }; @@ -212,8 +216,10 @@ public: [[nodiscard]] bool loadedAtBottom() const; [[nodiscard]] bool isEmpty() const; - [[nodiscard]] bool hasCopyRestriction() const; - [[nodiscard]] bool showCopyRestriction(); + [[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const; + [[nodiscard]] bool showCopyRestriction(HistoryItem *item = nullptr); + [[nodiscard]] bool hasCopyRestrictionForSelected() const; + [[nodiscard]] bool showCopyRestrictionForSelected(); [[nodiscard]] bool hasSelectRestriction() const; // AbstractTooltipShower interface @@ -617,7 +623,8 @@ void ConfirmForwardSelectedItems(not_null<ListWidget*> widget); void ConfirmSendNowSelectedItems(not_null<ListWidget*> widget); [[nodiscard]] CopyRestrictionType CopyRestrictionTypeFor( - not_null<PeerData*> peer); + not_null<PeerData*> peer, + HistoryItem *item = nullptr); [[nodiscard]] CopyRestrictionType SelectRestrictionTypeFor( not_null<PeerData*> peer); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 2840cd385..bf73a6ea2 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2217,7 +2217,7 @@ std::optional<QSize> Message::rightActionSize() const { bool Message::displayFastShare() const { const auto item = message(); const auto peer = item->history()->peer; - if (!item->isRegular() || !peer->allowsForwarding()) { + if (!item->allowsForward()) { return false; } else if (peer->isChannel()) { return !peer->isMegagroup(); diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index cd07eafad..3115e0e1f 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -676,8 +676,9 @@ not_null<Ui::ChatTheme*> PinnedWidget::listChatTheme() { return _theme.get(); } -CopyRestrictionType PinnedWidget::listCopyRestrictionType() { - return CopyRestrictionTypeFor(_history->peer); +CopyRestrictionType PinnedWidget::listCopyRestrictionType( + HistoryItem *item) { + return CopyRestrictionTypeFor(_history->peer, item); } CopyRestrictionType PinnedWidget::listSelectRestrictionType() { diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.h b/Telegram/SourceFiles/history/view/history_view_pinned_section.h index e36afffee..0021fdc24 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -103,7 +103,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null<UserData*> bot) override; not_null<Ui::ChatTheme*> listChatTheme() override; - CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listCopyRestrictionType(HistoryItem *item) override; CopyRestrictionType listSelectRestrictionType() override; protected: diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index e993acd31..bee37c0e4 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1921,8 +1921,9 @@ not_null<Ui::ChatTheme*> RepliesWidget::listChatTheme() { return _theme.get(); } -CopyRestrictionType RepliesWidget::listCopyRestrictionType() { - return CopyRestrictionTypeFor(_history->peer); +CopyRestrictionType RepliesWidget::listCopyRestrictionType( + HistoryItem *item) { + return CopyRestrictionTypeFor(_history->peer, item); } CopyRestrictionType RepliesWidget::listSelectRestrictionType() { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index f2700f364..b9c717202 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -139,7 +139,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null<UserData*> bot) override; not_null<Ui::ChatTheme*> listChatTheme() override; - CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listCopyRestrictionType(HistoryItem *item) override; CopyRestrictionType listSelectRestrictionType() override; protected: diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index e82966406..f7907e391 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -1232,7 +1232,8 @@ not_null<Ui::ChatTheme*> ScheduledWidget::listChatTheme() { return _theme.get(); } -CopyRestrictionType ScheduledWidget::listCopyRestrictionType() { +CopyRestrictionType ScheduledWidget::listCopyRestrictionType( + HistoryItem *item) { return CopyRestrictionType::None; } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 2352eb0c1..ae2e8bb57 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -120,7 +120,7 @@ public: const FullMsgId &context) override; void listHandleViaClick(not_null<UserData*> bot) override; not_null<Ui::ChatTheme*> listChatTheme() override; - CopyRestrictionType listCopyRestrictionType() override; + CopyRestrictionType listCopyRestrictionType(HistoryItem *item) override; CopyRestrictionType listSelectRestrictionType() override; protected: diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 5ef488dbb..93b1bdd8d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -593,10 +593,9 @@ void Document::ensureDataMediaCreated() const { bool Document::downloadInCorner() const { return _data->isAudioFile() - && _realParent->history()->peer->allowsForwarding() + && _realParent->allowsForward() && _data->canBeStreamed(_realParent) - && !_data->inappPlaybackFailed() - && _realParent->isRegular(); + && !_data->inappPlaybackFailed(); } void Document::drawCornerDownload( diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 6b5d07b9a..8d8d7119b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -264,10 +264,9 @@ QSize Gif::videoSize() const { bool Gif::downloadInCorner() const { return _data->isVideoFile() && (_data->loading() || !autoplayEnabled()) - && _realParent->history()->peer->allowsForwarding() + && _realParent->allowsForward() && _data->canBeStreamed(_realParent) - && !_data->inappPlaybackFailed() - && !_parent->data()->isSending(); + && !_data->inappPlaybackFailed(); } bool Gif::autoplayEnabled() const { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index b6e36b268..8cc5cfa2b 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -556,7 +556,8 @@ QSize OverlayWidget::flipSizeByRotation(QSize size) const { } bool OverlayWidget::hasCopyRestriction() const { - return _history && !_history->peer->allowsForwarding(); + return (_history && !_history->peer->allowsForwarding()) + || (_message && _message->forbidsForward()); } bool OverlayWidget::showCopyRestriction() { diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index e20eff936..b0a6834f6 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -969,10 +969,9 @@ Document::Document( bool Document::downloadInCorner() const { return _data->isAudioFile() - && parent()->history()->peer->allowsForwarding() + && parent()->allowsForward() && _data->canBeStreamed(parent()) - && !_data->inappPlaybackFailed() - && parent()->isRegular(); + && !_data->inappPlaybackFailed(); } void Document::initDimensions() { From 07ba84c8572b6dab1ab1d2800367d3eacba86e2b Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 19:24:08 +0400 Subject: [PATCH 101/180] Fix requesting bots list in megagroup. --- Telegram/SourceFiles/api/api_chat_participants.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index ee5c9da64..89edb5e32 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -411,7 +411,7 @@ void ChatParticipants::requestBots(not_null<ChannelData*> channel) { _botsRequests.remove(channel); result.match([&](const MTPDchannels_channelParticipants &data) { const auto &[availableCount, list] = Parse(channel, data); - ApplyLastList(channel, availableCount, list); + ApplyBotsList(channel, availableCount, list); }, [](const MTPDchannels_channelParticipantsNotModified &) { LOG(("API Error: " "channels.channelParticipantsNotModified received!")); @@ -518,6 +518,7 @@ ChatParticipants::Parsed ChatParticipants::Parse( not_null<ChannelData*> channel, const TLMembers &data) { channel->owner().processUsers(data.vusers()); + channel->owner().processChats(data.vchats()); auto list = ParseList(data, channel); if (channel->mgInfo) { RefreshChannelAdmins(channel, list); From fe4bb1935802b2eaca767491db6e43bbf82f9bf0 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 25 Nov 2021 19:46:37 +0400 Subject: [PATCH 102/180] Improve custom device name input design. --- Telegram/Resources/langs/lang.strings | 2 +- Telegram/SourceFiles/boxes/sessions_box.cpp | 33 +++++++++++++------- Telegram/SourceFiles/settings/settings.style | 16 ++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 59502c77b..1b5a92b5b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -700,7 +700,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_reset_one_sure" = "Do you want to terminate this session?"; "lng_settings_reset_button" = "Terminate"; "lng_settings_rename_device" = "Rename"; -"lng_settings_device_name" = "Device name ({device})"; +"lng_settings_device_name" = "Device name"; "lng_settings_rename_device_title" = "Rename current device"; "lng_settings_manage_local_storage" = "Manage local storage"; "lng_settings_ask_question" = "Ask a Question"; diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index aaa433a99..6a94829b2 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -16,10 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "lang/lang_keys.h" #include "main/main_session.h" -#include "styles/style_boxes.h" -#include "styles/style_info.h" -#include "styles/style_layers.h" -#include "styles/style_settings.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/labels.h" @@ -30,6 +26,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "core/core_settings.h" #include "window/window_session_controller.h" +#include "styles/style_boxes.h" +#include "styles/style_info.h" +#include "styles/style_layers.h" +#include "styles/style_settings.h" namespace { @@ -39,13 +39,24 @@ constexpr auto kMaxDeviceModelLength = 32; void RenameBox(not_null<Ui::GenericBox*> box) { box->setTitle(tr::lng_settings_rename_device_title()); - const auto name = box->addRow(object_ptr<Ui::InputField>( - box, - st::defaultInputField, - tr::lng_settings_device_name( - lt_device, - rpl::single(Platform::DeviceModelPretty())), - Core::App().settings().customDeviceModel())); + const auto skip = st::settingsSubsectionTitlePadding.top(); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + tr::lng_settings_device_name(), + st::settingsSubsectionTitle), + st::boxRowPadding + style::margins(0, skip, 0, 0)); + const auto name = box->addRow( + object_ptr<Ui::InputField>( + box, + st::settingsDeviceName, + rpl::single(Platform::DeviceModelPretty()), + Core::App().settings().customDeviceModel()), + st::boxRowPadding - style::margins( + st::settingsDeviceName.textMargins.left(), + 0, + st::settingsDeviceName.textMargins.right(), + 0)); name->setMaxLength(kMaxDeviceModelLength); box->setFocusCallback([=] { name->setFocusFast(); diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index eca3f69bf..e06edc15c 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -218,6 +218,22 @@ settingsAccentColorLine: 3px; settingsFilterIconSkip: 68px; settingsFilterIconLeft: 17px; +settingsDeviceName: InputField(defaultInputField) { + textBg: transparent; + textMargins: margins(1px, 3px, 1px, 4px); + + placeholderFg: placeholderFg; + placeholderFgActive: placeholderFgActive; + placeholderFgError: placeholderFgActive; + placeholderMargins: margins(1px, 0px, 1px, 0px); + placeholderScale: 0.; + placeholderFont: normalFont; + + heightMin: 29px; + + font: boxTextFont; +} + dictionariesSectionButton: SettingsButton(settingsUpdateToggle) { font: font(14px semibold); } From f3e1aef264c2164365a12ab8fb77b8127f30a6bd Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 26 Nov 2021 00:52:06 +0400 Subject: [PATCH 103/180] Allow editing sessions auto-termination period. --- Telegram/Resources/langs/lang.strings | 6 ++ .../SourceFiles/api/api_authorizations.cpp | 17 +++++ Telegram/SourceFiles/api/api_authorizations.h | 6 ++ .../boxes/self_destruction_box.cpp | 57 +++++++++++++--- .../SourceFiles/boxes/self_destruction_box.h | 6 ++ Telegram/SourceFiles/boxes/sessions_box.cpp | 65 +++++++++++++++---- Telegram/SourceFiles/boxes/sessions_box.h | 4 +- .../SourceFiles/core/local_url_handlers.cpp | 2 +- .../settings/settings_privacy_security.cpp | 3 +- 9 files changed, 143 insertions(+), 23 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1b5a92b5b..016a4ed1c 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -695,6 +695,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_destroy_if" = "If away for..."; "lng_settings_change_phone" = "Change phone number"; +"lng_settings_terminate_title" = "Terminate old sessions"; +"lng_settings_terminate_if" = "If inactive for..."; "lng_settings_reset" = "Terminate all other sessions"; "lng_settings_reset_sure" = "Are you sure you want to terminate\nall other sessions?"; "lng_settings_reset_one_sure" = "Do you want to terminate this session?"; @@ -816,6 +818,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_self_destruct_title" = "Account self-destruction"; "lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts."; +"lng_self_destruct_sessions_title" = "Session termination"; +"lng_self_destruct_sessions_description" = "If you don't come online from a specific session at least once within this period, it will be terminated."; +"lng_self_destruct_weeks#one" = "{count} week"; +"lng_self_destruct_weeks#other" = "{count} weeks"; "lng_self_destruct_months#one" = "{count} month"; "lng_self_destruct_months#other" = "{count} months"; "lng_self_destruct_years#one" = "{count} year"; diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index c645cb8cd..ce609661f 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -119,6 +119,7 @@ void Authorizations::reload() { _requestId = 0; _lastReceived = crl::now(); result.match([&](const MTPDaccount_authorizations &auths) { + _ttlDays = auths.vauthorization_ttl_days().v; _list = ( auths.vauthorizations().v ) | ranges::views::transform([](const MTPAuthorization &d) { @@ -184,6 +185,22 @@ rpl::producer<int> Authorizations::totalChanges() const { _listChanges.events() | rpl::map([=] { return total(); })); } +void Authorizations::updateTTL(int days) { + _api.request(_ttlRequestId).cancel(); + _ttlRequestId = _api.request(MTPaccount_SetAuthorizationTTL( + MTP_int(days) + )).done([=](const MTPBool &result) { + _ttlRequestId = 0; + }).fail([=](const MTP::Error &result) { + _ttlRequestId = 0; + }).send(); + _ttlDays = days; +} + +rpl::producer<int> Authorizations::ttlDays() const { + return _ttlDays.value() | rpl::filter(rpl::mappers::_1 != 0); +} + int Authorizations::total() const { return ranges::count_if( _list, diff --git a/Telegram/SourceFiles/api/api_authorizations.h b/Telegram/SourceFiles/api/api_authorizations.h index d84de7115..9110c0650 100644 --- a/Telegram/SourceFiles/api/api_authorizations.h +++ b/Telegram/SourceFiles/api/api_authorizations.h @@ -40,6 +40,9 @@ public: [[nodiscard]] int total() const; [[nodiscard]] rpl::producer<int> totalChanges() const; + void updateTTL(int days); + [[nodiscard]] rpl::producer<int> ttlDays() const; + private: MTP::Sender _api; mtpRequestId _requestId = 0; @@ -47,6 +50,9 @@ private: List _list; rpl::event_stream<> _listChanges; + mtpRequestId _ttlRequestId = 0; + rpl::variable<int> _ttlDays = 0; + crl::time _lastReceived = 0; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/boxes/self_destruction_box.cpp b/Telegram/SourceFiles/boxes/self_destruction_box.cpp index 9a167f6c4..2d4b94748 100644 --- a/Telegram/SourceFiles/boxes/self_destruction_box.cpp +++ b/Telegram/SourceFiles/boxes/self_destruction_box.cpp @@ -12,16 +12,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "apiwrap.h" #include "api/api_self_destruct.h" +#include "api/api_authorizations.h" #include "main/main_session.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" +namespace { + +using Type = SelfDestructionBox::Type; + +[[nodiscard]] std::vector<int> Values(Type type) { + switch (type) { + case Type::Account: return { 30, 90, 180, 365 }; + case Type::Sessions: return { 7, 30, 90, 180 }; + } + Unexpected("SelfDestructionBox::Type in Values."); +} + +} // namespace + SelfDestructionBox::SelfDestructionBox( QWidget*, not_null<Main::Session*> session, + Type type, rpl::producer<int> preloaded) -: _session(session) -, _ttlValues{ 30, 90, 180, 365 } +: _type(type) +, _session(session) +, _ttlValues(Values(type)) , _loading( this, tr::lng_contacts_loading(tr::now), @@ -57,7 +74,9 @@ void SelfDestructionBox::showContent() { auto y = st::boxOptionListPadding.top(); _description.create( this, - tr::lng_self_destruct_description(tr::now), + (_type == Type::Account + ? tr::lng_self_destruct_description(tr::now) + : tr::lng_self_destruct_sessions_description(tr::now)), st::boxLabel); _description->moveToLeft(st::boxPadding.left(), y); y += _description->height() + st::boxMediumSkip; @@ -76,24 +95,46 @@ void SelfDestructionBox::showContent() { clearButtons(); addButton(tr::lng_settings_save(), [=] { - _session->api().selfDestruct().update(_ttlGroup->value()); + switch (_type) { + case Type::Account: + _session->api().selfDestruct().update(_ttlGroup->value()); + break; + case Type::Sessions: + _session->api().authorizations().updateTTL(_ttlGroup->value()); + break; + } + closeBox(); }); addButton(tr::lng_cancel(), [=] { closeBox(); }); } QString SelfDestructionBox::DaysLabel(int days) { - return (days > 364) + return !days + ? QString() + : (days > 364) ? tr::lng_self_destruct_years(tr::now, lt_count, days / 365) - : tr::lng_self_destruct_months(tr::now, lt_count, qMax(days / 30, 1)); + : (days > 25) + ? tr::lng_self_destruct_months( + tr::now, + lt_count, + qMax(days / 30, 1)) + : tr::lng_self_destruct_weeks( + tr::now, + lt_count, + qMax(days / 7, 1)); } void SelfDestructionBox::prepare() { - setTitle(tr::lng_self_destruct_title()); + setTitle((_type == Type::Account + ? tr::lng_self_destruct_title() + : tr::lng_self_destruct_sessions_title())); auto fake = object_ptr<Ui::FlatLabel>( this, - tr::lng_self_destruct_description(tr::now), + (_type == Type::Account + ? tr::lng_self_destruct_description(tr::now) + : tr::lng_self_destruct_sessions_description(tr::now)), st::boxLabel); const auto boxHeight = st::boxOptionListPadding.top() + fake->height() + st::boxMediumSkip diff --git a/Telegram/SourceFiles/boxes/self_destruction_box.h b/Telegram/SourceFiles/boxes/self_destruction_box.h index 95f7928c8..228953aa6 100644 --- a/Telegram/SourceFiles/boxes/self_destruction_box.h +++ b/Telegram/SourceFiles/boxes/self_destruction_box.h @@ -22,9 +22,14 @@ class Session; class SelfDestructionBox : public Ui::BoxContent { public: + enum class Type { + Account, + Sessions, + }; SelfDestructionBox( QWidget*, not_null<Main::Session*> session, + Type type, rpl::producer<int> preloaded); static QString DaysLabel(int days); @@ -36,6 +41,7 @@ private: void gotCurrent(int days); void showContent(); + const Type _type; const not_null<Main::Session*> _session; bool _prepared = false; std::vector<int> _ttlValues; diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 6a94829b2..c4868bd89 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "base/algorithm.h" #include "base/platform/base_platform_info.h" +#include "boxes/self_destruction_box.h" #include "ui/boxes/confirm_box.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -77,7 +78,9 @@ void RenameBox(not_null<Ui::GenericBox*> box) { class SessionsContent : public Ui::RpWidget { public: - SessionsContent(QWidget*, not_null<Main::Session*> session); + SessionsContent( + QWidget*, + not_null<Window::SessionController*> controller); void setupContent(); @@ -165,7 +168,10 @@ private: class SessionsContent::Inner : public Ui::RpWidget { public: - Inner(QWidget *parent); + Inner( + QWidget *parent, + not_null<Window::SessionController*> controller, + rpl::producer<int> ttlDays); void showData(const Full &data); rpl::producer<uint64> terminateOne() const; @@ -177,16 +183,20 @@ public: private: void setupContent(); + const not_null<Window::SessionController*> _controller; QPointer<List> _current; QPointer<Ui::SettingsButton> _terminateAll; QPointer<List> _incomplete; QPointer<List> _list; + rpl::variable<int> _ttlDays; }; -SessionsContent::SessionsContent(QWidget*, not_null<Main::Session*> session) -: _authorizations(&session->api().authorizations()) -, _inner(this) +SessionsContent::SessionsContent( + QWidget*, + not_null<Window::SessionController*> controller) +: _authorizations(&controller->session().api().authorizations()) +, _inner(this, controller, _authorizations->ttlDays()) , _shortPollTimer([=] { shortPollSessions(); }) { } @@ -347,8 +357,13 @@ void SessionsContent::terminateAll() { terminate(std::move(callback), tr::lng_settings_reset_sure(tr::now)); } -SessionsContent::Inner::Inner(QWidget *parent) -: RpWidget(parent) { +SessionsContent::Inner::Inner( + QWidget *parent, + not_null<Window::SessionController*> controller, + rpl::producer<int> ttlDays) +: RpWidget(parent) +, _controller(controller) +, _ttlDays(std::move(ttlDays)) { setupContent(); } @@ -415,6 +430,31 @@ void SessionsContent::Inner::setupContent() { _list = listInner->add(object_ptr<List>(listInner)); AddSkip(listInner); + const auto ttlWrap = content->add( + object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( + content, + object_ptr<Ui::VerticalLayout>(content)))->setDuration(0); + const auto ttlInner = ttlWrap->entity(); + AddDivider(ttlInner); + AddSkip(ttlInner); + + AddSubsectionTitle(ttlInner, tr::lng_settings_terminate_title()); + + AddButtonWithLabel( + ttlInner, + tr::lng_settings_terminate_if(), + _ttlDays.value( + ) | rpl::map(SelfDestructionBox::DaysLabel), + st::settingsButton + )->addClickHandler([=] { + _controller->show(Box<SelfDestructionBox>( + &_controller->session(), + SelfDestructionBox::Type::Sessions, + _ttlDays.value())); + }); + + AddSkip(ttlInner); + const auto placeholder = content->add( object_ptr<Ui::SlideWrap<Ui::FlatLabel>>( content, @@ -431,6 +471,7 @@ void SessionsContent::Inner::setupContent() { (_1 + _2) > 0)); incompleteWrap->toggleOn(_incomplete->itemsCount() | rpl::map(_1 > 0)); listWrap->toggleOn(_list->itemsCount() | rpl::map(_1 > 0)); + ttlWrap->toggleOn(_list->itemsCount() | rpl::map(_1 > 0)); placeholder->toggleOn(_list->itemsCount() | rpl::map(_1 == 0)); Ui::ResizeFitChild(this, content); @@ -592,8 +633,10 @@ void SessionsContent::List::paintEvent(QPaintEvent *e) { } } -SessionsBox::SessionsBox(QWidget*, not_null<Main::Session*> session) -: _session(session) { +SessionsBox::SessionsBox( + QWidget*, + not_null<Window::SessionController*> controller) +: _controller(controller) { } void SessionsBox::prepare() { @@ -604,7 +647,7 @@ void SessionsBox::prepare() { const auto w = st::boxWideWidth; const auto content = setInnerWidget( - object_ptr<SessionsContent>(this, _session), + object_ptr<SessionsContent>(this, _controller), st::sessionsScroll); content->resize(w, st::noContactsHeight); content->setupContent(); @@ -624,7 +667,7 @@ Sessions::Sessions( void Sessions::setupContent(not_null<Window::SessionController*> controller) { const auto container = Ui::CreateChild<Ui::VerticalLayout>(this); const auto content = container->add( - object_ptr<SessionsContent>(container, &controller->session())); + object_ptr<SessionsContent>(container, controller)); content->setupContent(); Ui::ResizeFitChild(this, container); diff --git a/Telegram/SourceFiles/boxes/sessions_box.h b/Telegram/SourceFiles/boxes/sessions_box.h index bbde05411..ac6654015 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.h +++ b/Telegram/SourceFiles/boxes/sessions_box.h @@ -31,12 +31,12 @@ private: class SessionsBox : public Ui::BoxContent { public: - SessionsBox(QWidget*, not_null<Main::Session*> session); + SessionsBox(QWidget*, not_null<Window::SessionController*> controller); protected: void prepare() override; private: - const not_null<Main::Session*> _session; + const not_null<Window::SessionController*> _controller; }; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index e45295f4e..59d8c1bfb 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -379,7 +379,7 @@ bool ResolveSettings( } if (section == qstr("devices")) { controller->session().api().authorizations().reload(); - controller->show(Box<SessionsBox>(&controller->session())); + controller->show(Box<SessionsBox>(controller)); return true; } else if (section == qstr("language")) { ShowLanguagesBox(); diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index b288a0079..049ea5c48 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -693,6 +693,7 @@ void SetupSelfDestruction( )->addClickHandler([=] { controller->show(Box<SelfDestructionBox>( session, + SelfDestructionBox::Type::Account, session->api().selfDestruct().days())); }); @@ -800,7 +801,7 @@ void SetupSessionsList( std::move(count), st::settingsButton )->addClickHandler([=] { - controller->show(Box<SessionsBox>(&controller->session())); + controller->show(Box<SessionsBox>(controller)); }); AddSkip(container, st::settingsPrivacySecurityPadding); AddDividerText(container, tr::lng_settings_sessions_about()); From 598cec8a9da06617b9064c513bfde8e5fa961871 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 26 Nov 2021 17:01:37 +0400 Subject: [PATCH 104/180] Show session details on click. --- Telegram/Resources/langs/lang.strings | 6 + .../SourceFiles/api/api_authorizations.cpp | 7 +- Telegram/SourceFiles/api/api_authorizations.h | 2 +- Telegram/SourceFiles/boxes/sessions_box.cpp | 148 +++++++++++++++--- 4 files changed, 135 insertions(+), 28 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 016a4ed1c..ea9c3e095 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -723,6 +723,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sessions_terminate_all_about" = "Logs out all devices except for this one."; "lng_sessions_incomplete" = "Incomplete login attempts"; "lng_sessions_incomplete_about" = "The devices above have no access to your messages. The code was entered correctly, but no correct password was given."; +"lng_sessions_terminate" = "Terminate"; +"lng_sessions_application" = "Application"; +"lng_sessions_system" = "System Version"; +"lng_sessions_ip" = "IP Address"; +"lng_sessions_location" = "Location"; +"lng_sessions_location_about" = "This location estimate is based on the IP address and may not always be accurate."; "lng_blocked_list_title" = "Blocked users"; "lng_blocked_list_unknown_phone" = "unknown phone number"; diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index ce609661f..9764f5d5a 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -58,7 +58,7 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { //if (j != countries.cend()) { // country = QString::fromUtf8(j.value()->name); //} - + result.system = qs(data.vsystem_version()); result.activeTime = data.vdate_active().v ? data.vdate_active().v : data.vdate_created().v; @@ -82,10 +82,7 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { result.active = lastDate.toString(cDateFormat()); } } - result.location = (country.isEmpty() ? result.ip : country) - + (result.hash - ? (QString::fromUtf8(" \xe2\x80\x93 ") + result.active) - : QString()); + result.location = country; return result; } diff --git a/Telegram/SourceFiles/api/api_authorizations.h b/Telegram/SourceFiles/api/api_authorizations.h index 9110c0650..4b28e9c17 100644 --- a/Telegram/SourceFiles/api/api_authorizations.h +++ b/Telegram/SourceFiles/api/api_authorizations.h @@ -22,7 +22,7 @@ public: bool incomplete = false; TimeId activeTime = 0; - QString name, active, info, ip, location; + QString name, active, info, ip, location, system; }; using List = std::vector<Entry>; diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index c4868bd89..d398dac53 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -37,6 +37,8 @@ namespace { constexpr auto kSessionsShortPollTimeout = 60 * crl::time(1000); constexpr auto kMaxDeviceModelLength = 32; +using EntryData = Api::Authorizations::Entry; + void RenameBox(not_null<Ui::GenericBox*> box) { box->setTitle(tr::lng_settings_rename_device_title()); @@ -74,6 +76,63 @@ void RenameBox(not_null<Ui::GenericBox*> box) { box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } +void SessionInfoBox( + not_null<Ui::GenericBox*> box, + const EntryData &data, + Fn<void(uint64)> terminate) { + box->setTitle(rpl::single(data.name)); + box->setWidth(st::boxWidth); + + const auto skips = style::margins(0, 0, 0, st::settingsSectionSkip); + const auto date = base::unixtime::parse(data.activeTime); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + rpl::single(langDateTimeFull(date)), + st::boxDividerLabel), + st::boxRowPadding + skips); + + const auto add = [&](rpl::producer<QString> label, QString value) { + if (value.isEmpty()) { + return; + } + Settings::AddSubsectionTitle( + box->verticalLayout(), + std::move(label)); + box->addRow( + object_ptr<Ui::FlatLabel>( + box, + rpl::single(value), + st::boxDividerLabel), + st::boxRowPadding + skips); + }; + add(tr::lng_sessions_application(), data.info); + add(tr::lng_sessions_system(), data.system); + add(tr::lng_sessions_ip(), data.ip); + add(tr::lng_sessions_location(), data.location); + if (!data.location.isEmpty()) { + Settings::AddDividerText( + box->verticalLayout(), + tr::lng_sessions_location_about()); + } + + box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); + box->addLeftButton(tr::lng_sessions_terminate(), [=, hash = data.hash] { + const auto weak = Ui::MakeWeak(box.get()); + terminate(hash); + if (weak) { + box->closeBox(); + } + }, st::attentionBoxButton); +} + +[[nodiscard]] QString LocationAndDate(const EntryData &entry) { + return (entry.location.isEmpty() ? entry.ip : entry.location) + + (entry.hash + ? (QString::fromUtf8(" \xe2\x80\x93 ") + entry.active) + : QString()); +} + } // namespace class SessionsContent : public Ui::RpWidget { @@ -91,20 +150,20 @@ protected: private: struct Entry { Entry() = default; - Entry(const Api::Authorizations::Entry &entry) - : hash(entry.hash) + Entry(const EntryData &entry) + : data(entry) , incomplete(entry.incomplete) , activeTime(entry.activeTime) , name(st::sessionNameStyle, entry.name) , info(st::sessionInfoStyle, entry.info) - , ip(st::sessionInfoStyle, entry.location) { + , location(st::sessionInfoStyle, LocationAndDate(entry)) { }; - uint64 hash = 0; + EntryData data; bool incomplete = false; TimeId activeTime = 0; - Ui::Text::String name, info, ip; + Ui::Text::String name, info, location; }; struct Full { Entry current; @@ -121,6 +180,7 @@ private: void terminateOne(uint64 hash); void terminateAll(); + const not_null<Window::SessionController*> _controller; const not_null<Api::Authorizations*> _authorizations; rpl::variable<bool> _loading = false; @@ -139,7 +199,8 @@ public: void showData(gsl::span<const Entry> items); rpl::producer<int> itemsCount() const; - rpl::producer<uint64> terminate() const; + rpl::producer<uint64> terminateRequests() const; + [[nodiscard]] rpl::producer<EntryData> showRequests() const; void terminating(uint64 hash, bool terminating); @@ -147,6 +208,9 @@ protected: void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + int resizeGetHeight(int newWidth) override; private: @@ -161,8 +225,11 @@ private: RowWidth _rowWidth; std::vector<Entry> _items; std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons; - rpl::event_stream<uint64> _terminate; + rpl::event_stream<uint64> _terminateRequests; rpl::event_stream<int> _itemsCount; + rpl::event_stream<EntryData> _showRequests; + + int _pressed = -1; }; @@ -174,9 +241,10 @@ public: rpl::producer<int> ttlDays); void showData(const Full &data); - rpl::producer<uint64> terminateOne() const; - rpl::producer<> terminateAll() const; - rpl::producer<> renameCurrentRequests() const; + [[nodiscard]] rpl::producer<EntryData> showRequests() const; + [[nodiscard]] rpl::producer<uint64> terminateOne() const; + [[nodiscard]] rpl::producer<> terminateAll() const; + [[nodiscard]] rpl::producer<> renameCurrentRequests() const; void terminatingOne(uint64 hash, bool terminating); @@ -195,7 +263,8 @@ private: SessionsContent::SessionsContent( QWidget*, not_null<Window::SessionController*> controller) -: _authorizations(&controller->session().api().authorizations()) +: _controller(controller) +, _authorizations(&controller->session().api().authorizations()) , _inner(this, controller, _authorizations->ttlDays()) , _shortPollTimer([=] { shortPollSessions(); }) { } @@ -209,6 +278,14 @@ void SessionsContent::setupContent() { resize(width(), height); }, _inner->lifetime()); + _inner->showRequests( + ) | rpl::start_with_next([=](const EntryData &data) { + _controller->show(Box( + SessionInfoBox, + data, + [=](uint64 hash) { terminateOne(hash); })); + }, lifetime()); + _inner->terminateOne( ) | rpl::start_with_next([=](uint64 hash) { terminateOne(hash); @@ -240,7 +317,7 @@ void SessionsContent::parse(const Api::Authorizations::List &list) { _data = Full(); for (const auto &auth : list) { auto entry = Entry(auth); - if (!entry.hash) { + if (!entry.data.hash) { _data.current = std::move(entry); } else if (entry.incomplete) { _data.incomplete.push_back(std::move(entry)); @@ -323,7 +400,10 @@ void SessionsContent::terminateOne(uint64 hash) { _inner->terminatingOne(hash, false); const auto removeByHash = [&](std::vector<Entry> &list) { list.erase( - ranges::remove(list, hash, &Entry::hash), + ranges::remove( + list, + hash, + [](const Entry &entry) { return entry.data.hash; }), end(list)); }; removeByHash(_data.incomplete); @@ -489,8 +569,14 @@ rpl::producer<> SessionsContent::Inner::terminateAll() const { rpl::producer<uint64> SessionsContent::Inner::terminateOne() const { return rpl::merge( - _incomplete->terminate(), - _list->terminate()); + _incomplete->terminateRequests(), + _list->terminateRequests()); +} + +rpl::producer<EntryData> SessionsContent::Inner::showRequests() const { + return rpl::merge( + _incomplete->showRequests(), + _list->showRequests()); } void SessionsContent::Inner::terminatingOne(uint64 hash, bool terminating) { @@ -512,7 +598,7 @@ void SessionsContent::List::subscribeToCustomDeviceModel() { Core::App().settings().deviceModelChanges( ) | rpl::start_with_next([=](const QString &model) { for (auto &entry : _items) { - if (!entry.hash) { + if (!entry.data.hash) { entry.name.setText(st::sessionNameStyle, model); } } @@ -527,7 +613,7 @@ void SessionsContent::List::showData(gsl::span<const Entry> items) { _items.clear(); _items.insert(begin(_items), items.begin(), items.end()); for (const auto &entry : _items) { - const auto hash = entry.hash; + const auto hash = entry.data.hash; if (!hash) { continue; } @@ -542,7 +628,7 @@ void SessionsContent::List::showData(gsl::span<const Entry> items) { st::sessionTerminate))).first->second.get(); }(); button->setClickedCallback([=] { - _terminate.fire_copy(hash); + _terminateRequests.fire_copy(hash); }); button->show(); const auto number = _terminateButtons.size() - 1; @@ -557,12 +643,16 @@ void SessionsContent::List::showData(gsl::span<const Entry> items) { _itemsCount.fire(_items.size()); } +rpl::producer<EntryData> SessionsContent::List::showRequests() const { + return _showRequests.events(); +} + rpl::producer<int> SessionsContent::List::itemsCount() const { return _itemsCount.events_starting_with(_items.size()); } -rpl::producer<uint64> SessionsContent::List::terminate() const { - return _terminate.events(); +rpl::producer<uint64> SessionsContent::List::terminateRequests() const { + return _terminateRequests.events(); } void SessionsContent::List::terminating(uint64 hash, bool terminating) { @@ -617,7 +707,7 @@ void SessionsContent::List::paintEvent(QPaintEvent *e) { const auto nameW = _rowWidth.info; const auto nameH = entry.name.style()->font->height; - const auto infoW = entry.hash ? _rowWidth.info : available; + const auto infoW = entry.data.hash ? _rowWidth.info : available; const auto infoH = entry.info.style()->font->height; p.setPen(st::sessionNameFg); @@ -627,12 +717,26 @@ void SessionsContent::List::paintEvent(QPaintEvent *e) { entry.info.drawLeftElided(p, x, y + nameH, infoW, w); p.setPen(st::sessionInfoFg); - entry.ip.drawLeftElided(p, x, y + nameH + infoH, available, w); + entry.location.drawLeftElided(p, x, y + nameH + infoH, available, w); p.translate(0, st::sessionHeight); } } +void SessionsContent::List::mousePressEvent(QMouseEvent *e) { + const auto index = e->pos().y() / st::sessionHeight; + _pressed = (index >= 0 && index < _items.size()) ? index : -1; +} + +void SessionsContent::List::mouseReleaseEvent(QMouseEvent *e) { + const auto index = e->pos().y() / st::sessionHeight; + const auto released = (index >= 0 && index < _items.size()) ? index : -1; + if (released == _pressed && released >= 0) { + _showRequests.fire_copy(_items[released].data); + } + _pressed = -1; +} + SessionsBox::SessionsBox( QWidget*, not_null<Window::SessionController*> controller) From 826496871841e8a19685e1449cc119e657d7a160 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 26 Nov 2021 18:01:58 +0400 Subject: [PATCH 105/180] Use cloud "disable calls" settings instead of local. --- .../SourceFiles/api/api_authorizations.cpp | 37 +++++++++++++++++++ Telegram/SourceFiles/api/api_authorizations.h | 11 ++++++ Telegram/SourceFiles/calls/calls_instance.cpp | 2 - Telegram/SourceFiles/core/core_settings.cpp | 10 ++--- Telegram/SourceFiles/core/core_settings.h | 9 ++--- .../SourceFiles/settings/settings_calls.cpp | 20 ++++++---- .../settings/settings_notifications.cpp | 21 +++++++---- 7 files changed, 81 insertions(+), 29 deletions(-) diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index 9764f5d5a..0f1f457f8 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -104,6 +104,10 @@ Authorizations::Authorizations(not_null<ApiWrap*> api) _listChanges.fire({}); } }, _lifetime); + + if (Core::App().settings().disableCallsLegacy()) { + toggleCallsDisabledHere(true); + } } void Authorizations::reload() { @@ -198,6 +202,39 @@ rpl::producer<int> Authorizations::ttlDays() const { return _ttlDays.value() | rpl::filter(rpl::mappers::_1 != 0); } +void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) { + if (const auto sent = _toggleCallsDisabledRequests.take(hash)) { + _api.request(*sent).cancel(); + } + using Flag = MTPaccount_ChangeAuthorizationSettings::Flag; + const auto id = _api.request(MTPaccount_ChangeAuthorizationSettings( + MTP_flags(Flag::f_call_requests_disabled), + MTP_long(hash), + MTPBool(), // encrypted_requests_disabled + MTP_bool(disabled) + )).done([=](const MTPBool &) { + _toggleCallsDisabledRequests.remove(hash); + }).fail([=](const MTP::Error &) { + _toggleCallsDisabledRequests.remove(hash); + }).send(); + _toggleCallsDisabledRequests.emplace(hash, id); + if (!hash) { + _callsDisabledHere = disabled; + } +} + +bool Authorizations::callsDisabledHere() const { + return _callsDisabledHere.current(); +} + +rpl::producer<bool> Authorizations::callsDisabledHereValue() const { + return _callsDisabledHere.value(); +} + +rpl::producer<bool> Authorizations::callsDisabledHereChanges() const { + return _callsDisabledHere.changes(); +} + int Authorizations::total() const { return ranges::count_if( _list, diff --git a/Telegram/SourceFiles/api/api_authorizations.h b/Telegram/SourceFiles/api/api_authorizations.h index 4b28e9c17..45092eca2 100644 --- a/Telegram/SourceFiles/api/api_authorizations.h +++ b/Telegram/SourceFiles/api/api_authorizations.h @@ -43,6 +43,14 @@ public: void updateTTL(int days); [[nodiscard]] rpl::producer<int> ttlDays() const; + void toggleCallsDisabledHere(bool disabled) { + toggleCallsDisabled(0, disabled); + } + void toggleCallsDisabled(uint64 hash, bool disabled); + [[nodiscard]] bool callsDisabledHere() const; + [[nodiscard]] rpl::producer<bool> callsDisabledHereValue() const; + [[nodiscard]] rpl::producer<bool> callsDisabledHereChanges() const; + private: MTP::Sender _api; mtpRequestId _requestId = 0; @@ -53,6 +61,9 @@ private: mtpRequestId _ttlRequestId = 0; rpl::variable<int> _ttlDays = 0; + base::flat_map<uint64, mtpRequestId> _toggleCallsDisabledRequests; + rpl::variable<bool> _callsDisabledHere; + crl::time _lastReceived = 0; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 874126fc5..3d1750a12 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -503,8 +503,6 @@ void Instance::handleCallUpdate( } else if (phoneCall.vdate().v + (config.callRingTimeoutMs / 1000) < base::unixtime::now()) { LOG(("Ignoring too old call.")); - } else if (Core::App().settings().disableCalls()) { - LOG(("Ignoring call because of 'accept calls' settings.")); } else { createCall(user, Call::Type::Incoming, phoneCall.is_video()); _currentCall->handleUpdate(call); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 1863fb27d..ca15ba2cf 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -203,7 +203,7 @@ QByteArray Settings::serialize() const { << _groupCallPushToTalkShortcut << qint64(_groupCallPushToTalkDelay) << qint32(0) // Call audio backend - << qint32(_disableCalls ? 1 : 0) + << qint32(0) // Legacy disable calls, now in session settings << windowPosition << qint32(recentEmojiPreloadData.size()); for (const auto &[id, rating] : recentEmojiPreloadData) { @@ -300,7 +300,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { QByteArray groupCallPushToTalkShortcut = _groupCallPushToTalkShortcut; qint64 groupCallPushToTalkDelay = _groupCallPushToTalkDelay; qint32 callAudioBackend = 0; - qint32 disableCalls = _disableCalls ? 1 : 0; + qint32 disableCallsLegacy = 0; QByteArray windowPosition; std::vector<RecentEmojiId> recentEmojiPreload; base::flat_map<QString, uint8> emojiVariants; @@ -413,7 +413,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { stream >> callAudioBackend; } if (!stream.atEnd()) { - stream >> disableCalls; + stream >> disableCallsLegacy; } if (!stream.atEnd()) { stream >> windowPosition; @@ -593,7 +593,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { _groupCallPushToTalk = (groupCallPushToTalk == 1); _groupCallPushToTalkShortcut = groupCallPushToTalkShortcut; _groupCallPushToTalkDelay = groupCallPushToTalkDelay; - _disableCalls = (disableCalls == 1); + _disableCallsLegacy = (disableCallsLegacy == 1); if (!windowPosition.isEmpty()) { _windowPosition = Deserialize(windowPosition); } @@ -870,7 +870,7 @@ void Settings::resetOnLastLogout() { //_callInputVolume = 100; //_callAudioDuckingEnabled = true; - _disableCalls = false; + _disableCallsLegacy = false; _groupCallPushToTalk = false; _groupCallPushToTalkShortcut = QByteArray(); diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index b36112fa4..b057a1f64 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -265,11 +265,8 @@ public: _callAudioDuckingEnabled = value; } [[nodiscard]] Webrtc::Backend callAudioBackend() const; - void setDisableCalls(bool value) { - _disableCalls = value; - } - [[nodiscard]] bool disableCalls() const { - return _disableCalls; + [[nodiscard]] bool disableCallsLegacy() const { + return _disableCallsLegacy; } [[nodiscard]] bool groupCallPushToTalk() const { return _groupCallPushToTalk; @@ -714,7 +711,7 @@ private: int _callOutputVolume = 100; int _callInputVolume = 100; bool _callAudioDuckingEnabled = true; - bool _disableCalls = false; + bool _disableCallsLegacy = false; bool _groupCallPushToTalk = false; bool _groupCallNoiseSuppression = false; QByteArray _groupCallPushToTalkShortcut; diff --git a/Telegram/SourceFiles/settings/settings_calls.cpp b/Telegram/SourceFiles/settings/settings_calls.cpp index df6dbdeb5..fb0ec060d 100644 --- a/Telegram/SourceFiles/settings/settings_calls.cpp +++ b/Telegram/SourceFiles/settings/settings_calls.cpp @@ -27,6 +27,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_call.h" #include "calls/calls_instance.h" #include "calls/calls_video_bubble.h" +#include "apiwrap.h" +#include "api/api_authorizations.h" #include "webrtc/webrtc_media_devices.h" #include "webrtc/webrtc_video_track.h" #include "webrtc/webrtc_audio_input_tester.h" @@ -268,19 +270,21 @@ void Calls::setupContent() { AddSkip(content); AddSubsectionTitle(content, tr::lng_settings_call_section_other()); + const auto api = &_controller->session().api(); AddButton( content, tr::lng_settings_call_accept_calls(), st::settingsButton - )->toggleOn(rpl::single( - !settings.disableCalls() - ))->toggledChanges( - ) | rpl::filter([&settings](bool value) { - return (settings.disableCalls() == value); - }) | rpl::start_with_next([=](bool value) { - Core::App().settings().setDisableCalls(!value); - Core::App().saveSettingsDelayed(); + )->toggleOn( + api->authorizations().callsDisabledHereValue( + ) | rpl::map(!rpl::mappers::_1) + )->toggledChanges( + ) | rpl::filter([=](bool value) { + return (value == api->authorizations().callsDisabledHere()); + }) | start_with_next([=](bool value) { + api->authorizations().toggleCallsDisabledHere(!value); }, content->lifetime()); + AddButton( content, tr::lng_settings_call_open_system_prefs(), diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 9b477ef48..7b45f1c9c 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "main/main_account.h" #include "main/main_domain.h" +#include "api/api_authorizations.h" #include "apiwrap.h" #include "facades.h" #include "styles/style_settings.h" @@ -670,15 +671,19 @@ void SetupNotificationsContent( AddSubsectionTitle( container, tr::lng_settings_notifications_calls_title()); - addCheckbox( + const auto authorizations = &session->api().authorizations(); + const auto acceptCalls = addCheckbox( tr::lng_settings_call_accept_calls(tr::now), - !settings.disableCalls() - )->checkedChanges( - ) | rpl::filter([&settings](bool value) { - return (settings.disableCalls() == value); - }) | rpl::start_with_next([=](bool value) { - Core::App().settings().setDisableCalls(!value); - Core::App().saveSettingsDelayed(); + !authorizations->callsDisabledHere()); + session->api().authorizations().callsDisabledHereChanges( + ) | rpl::start_with_next([=](bool disabled) { + acceptCalls->setChecked( + !disabled, + Ui::Checkbox::NotifyAboutChange::DontNotify); + }, acceptCalls->lifetime()); + acceptCalls->checkedChanges( + ) | rpl::start_with_next([=](bool value) { + authorizations->toggleCallsDisabledHere(!value); }, container->lifetime()); AddSkip(container, st::settingsCheckboxesSkip); From 75fc88d679c27316af54af16dfa67103fded7dd5 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 26 Nov 2021 18:13:58 +0400 Subject: [PATCH 106/180] Use VS 2022 with 10.0.22000.0 SDK version. --- cmake | 2 +- docs/building-win-x64.md | 12 +++++++----- docs/building-win.md | 12 +++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/cmake b/cmake index 344074817..7332624b8 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 34407481713f987ea8b74bb949fd3d79e123e3c7 +Subproject commit 7332624b87233eee6d15d7da0e09d0287626d466 diff --git a/docs/building-win-x64.md b/docs/building-win-x64.md index 734d0aded..7c7fa8280 100644 --- a/docs/building-win-x64.md +++ b/docs/building-win-x64.md @@ -8,9 +8,11 @@ ## Prepare folder +The build is done in **Visual Studio 2022** with **10.0.22000.0** SDK version. + Choose an empty folder for the future build, for example **D:\\TBuild**. It will be named ***BuildPath*** in the rest of this document. Create two folders there, ***BuildPath*\\ThirdParty** and ***BuildPath*\\Libraries**. -All commands (if not stated otherwise) will be launched from **x64 Native Tools Command Prompt for VS 2019.bat** (should be in **Start Menu > Visual Studio 2019** menu folder). Pay attention not to use any other Command Prompt. +All commands (if not stated otherwise) will be launched from **x64 Native Tools Command Prompt for VS 2022.bat** (should be in **Start Menu > Visual Studio 2022** menu folder). Pay attention not to use any other Command Prompt. ### Obtain your API credentials @@ -24,7 +26,7 @@ You will require **api_id** and **api_hash** to access the Telegram API servers. * Download **MSYS2** installer from [http://www.msys2.org/](http://www.msys2.org/) and install to ***BuildPath*\\ThirdParty\\msys64** * Download **jom** archive from [http://download.qt.io/official_releases/jom/jom.zip](http://download.qt.io/official_releases/jom/jom.zip) and unpack to ***BuildPath*\\ThirdParty\\jom** * Download **Python 3.9** installer from [https://www.python.org/downloads/](https://www.python.org/downloads/) and install to ***BuildPath*\\ThirdParty\\Python39** -* Download **CMake** installer from [https://cmake.org/download/](https://cmake.org/download/) and install to ***BuildPath*\\ThirdParty\\cmake** +* Download **CMake 3.21 or later** installer from [https://cmake.org/download/](https://cmake.org/download/) and install to ***BuildPath*\\ThirdParty\\cmake** * Download **Ninja** executable from [https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-win.zip](https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-win.zip) and unpack to ***BuildPath*\\ThirdParty\\Ninja** * Download **Git** installer from [https://git-scm.com/download/win](https://git-scm.com/download/win) and install it. * Download **NuGet** executable from [https://dist.nuget.org/win-x86-commandline/latest/nuget.exe](https://dist.nuget.org/win-x86-commandline/latest/nuget.exe) and put to ***BuildPath*\\ThirdParty\\NuGet** @@ -38,13 +40,13 @@ Add **Python 3.9** and **NuGet** to your PATH: * Add ***BuildPath*\\ThirdParty\\Python39** value. * Add ***BuildPath*\\ThirdParty\\NuGet** value. -Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** and run +Open **x64 Native Tools Command Prompt for VS 2022.bat**, go to ***BuildPath*** and run python -m pip install pywin32 ## Clone source code and prepare libraries -Open **x64 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** and run +Open **x64 Native Tools Command Prompt for VS 2022.bat**, go to ***BuildPath*** and run git clone --recursive https://github.com/telegramdesktop/tdesktop.git tdesktop\Telegram\build\prepare\win.bat @@ -55,7 +57,7 @@ Go to ***BuildPath*\\tdesktop\\Telegram** and run (using [your **api_id** and ** configure.bat x64 -D TDESKTOP_API_ID=YOUR_API_ID -D TDESKTOP_API_HASH=YOUR_API_HASH -D DESKTOP_APP_USE_PACKAGED=OFF -D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF -* Open ***BuildPath*\\tdesktop\\out\\Telegram.sln** in Visual Studio 2019 +* Open ***BuildPath*\\tdesktop\\out\\Telegram.sln** in Visual Studio 2022 * Select Telegram project and press Build > Build Telegram (Debug and Release configurations) * The result Telegram.exe will be located in **D:\TBuild\tdesktop\out\Debug** (and **Release**) diff --git a/docs/building-win.md b/docs/building-win.md index 0c9af5d7b..bc7d246fc 100644 --- a/docs/building-win.md +++ b/docs/building-win.md @@ -8,9 +8,11 @@ ## Prepare folder +The build is done in **Visual Studio 2022** with **10.0.22000.0** SDK version. + Choose an empty folder for the future build, for example **D:\\TBuild**. It will be named ***BuildPath*** in the rest of this document. Create two folders there, ***BuildPath*\\ThirdParty** and ***BuildPath*\\Libraries**. -All commands (if not stated otherwise) will be launched from **x86 Native Tools Command Prompt for VS 2019.bat** (should be in **Start Menu > Visual Studio 2019** menu folder). Pay attention not to use any other Command Prompt. +All commands (if not stated otherwise) will be launched from **x86 Native Tools Command Prompt for VS 2022.bat** (should be in **Start Menu > Visual Studio 2022** menu folder). Pay attention not to use any other Command Prompt. ### Obtain your API credentials @@ -24,7 +26,7 @@ You will require **api_id** and **api_hash** to access the Telegram API servers. * Download **MSYS2** installer from [http://www.msys2.org/](http://www.msys2.org/) and install to ***BuildPath*\\ThirdParty\\msys64** * Download **jom** archive from [http://download.qt.io/official_releases/jom/jom.zip](http://download.qt.io/official_releases/jom/jom.zip) and unpack to ***BuildPath*\\ThirdParty\\jom** * Download **Python 3.9** installer from [https://www.python.org/downloads/](https://www.python.org/downloads/) and install to ***BuildPath*\\ThirdParty\\Python39** -* Download **CMake** installer from [https://cmake.org/download/](https://cmake.org/download/) and install to ***BuildPath*\\ThirdParty\\cmake** +* Download **CMake 3.21 or later** installer from [https://cmake.org/download/](https://cmake.org/download/) and install to ***BuildPath*\\ThirdParty\\cmake** * Download **Ninja** executable from [https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-win.zip](https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-win.zip) and unpack to ***BuildPath*\\ThirdParty\\Ninja** * Download **Git** installer from [https://git-scm.com/download/win](https://git-scm.com/download/win) and install it. * Download **NuGet** executable from [https://dist.nuget.org/win-x86-commandline/latest/nuget.exe](https://dist.nuget.org/win-x86-commandline/latest/nuget.exe) and put to ***BuildPath*\\ThirdParty\\NuGet** @@ -38,13 +40,13 @@ Add **Python 3.9** and **NuGet** to your PATH: * Add ***BuildPath*\\ThirdParty\\Python39** value. * Add ***BuildPath*\\ThirdParty\\NuGet** value. -Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** and run +Open **x86 Native Tools Command Prompt for VS 2022.bat**, go to ***BuildPath*** and run python -m pip install pywin32 ## Clone source code and prepare libraries -Open **x86 Native Tools Command Prompt for VS 2019.bat**, go to ***BuildPath*** and run +Open **x86 Native Tools Command Prompt for VS 2022.bat**, go to ***BuildPath*** and run git clone --recursive https://github.com/telegramdesktop/tdesktop.git tdesktop\Telegram\build\prepare\win.bat @@ -55,7 +57,7 @@ Go to ***BuildPath*\\tdesktop\\Telegram** and run (using [your **api_id** and ** configure.bat -D TDESKTOP_API_ID=YOUR_API_ID -D TDESKTOP_API_HASH=YOUR_API_HASH -D DESKTOP_APP_USE_PACKAGED=OFF -D DESKTOP_APP_DISABLE_CRASH_REPORTS=OFF -* Open ***BuildPath*\\tdesktop\\out\\Telegram.sln** in Visual Studio 2019 +* Open ***BuildPath*\\tdesktop\\out\\Telegram.sln** in Visual Studio 2022 * Select Telegram project and press Build > Build Telegram (Debug and Release configurations) * The result Telegram.exe will be located in **D:\TBuild\tdesktop\out\Debug** (and **Release**) From 53305f5f462ca141eedfd2eb2757b8d52b6bd8a3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Thu, 25 Nov 2021 09:19:16 +0400 Subject: [PATCH 107/180] Fix paste check in Linux global menu --- Telegram/SourceFiles/platform/linux/main_window_linux.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 54f2c9408..6eb52da55 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <QtCore/QSize> #include <QtCore/QTemporaryFile> +#include <QtCore/QMimeData> #include <QtGui/QWindow> #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION @@ -1299,8 +1300,8 @@ void MainWindow::updateGlobalMenuHook() { auto canPaste = false; auto canDelete = false; auto canSelectAll = false; - const auto clipboardHasText = QGuiApplication::clipboard() - ->ownsClipboard(); + const auto mimeData = QGuiApplication::clipboard()->mimeData(); + const auto clipboardHasText = mimeData ? mimeData->hasText() : false; auto markdownEnabled = false; if (const auto edit = qobject_cast<QLineEdit*>(focused)) { canCut = canCopy = canDelete = edit->hasSelectedText(); From 8fda1169e5a7df458e87ac0ed20a9516f0358e73 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Thu, 25 Nov 2021 09:19:38 +0400 Subject: [PATCH 108/180] Get rid of Platform::MainWindow::psLinux* --- .../platform/linux/main_window_linux.cpp | 107 +++++------------- .../platform/linux/main_window_linux.h | 15 --- 2 files changed, 30 insertions(+), 92 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 6eb52da55..88066f224 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -1069,84 +1069,89 @@ void MainWindow::createGlobalMenu() { psUndo = edit->addAction( tr::lng_linux_menu_undo(tr::now), - this, - [=] { psLinuxUndo(); }, + [] { SendKeySequence(Qt::Key_Z, Qt::ControlModifier); }, QKeySequence::Undo); psRedo = edit->addAction( tr::lng_linux_menu_redo(tr::now), - this, - [=] { psLinuxRedo(); }, + [] { + SendKeySequence( + Qt::Key_Z, + Qt::ControlModifier | Qt::ShiftModifier); + }, QKeySequence::Redo); edit->addSeparator(); psCut = edit->addAction( tr::lng_mac_menu_cut(tr::now), - this, - [=] { psLinuxCut(); }, + [] { SendKeySequence(Qt::Key_X, Qt::ControlModifier); }, QKeySequence::Cut); + psCopy = edit->addAction( tr::lng_mac_menu_copy(tr::now), - this, - [=] { psLinuxCopy(); }, + [] { SendKeySequence(Qt::Key_C, Qt::ControlModifier); }, QKeySequence::Copy); psPaste = edit->addAction( tr::lng_mac_menu_paste(tr::now), - this, - [=] { psLinuxPaste(); }, + [] { SendKeySequence(Qt::Key_V, Qt::ControlModifier); }, QKeySequence::Paste); psDelete = edit->addAction( tr::lng_mac_menu_delete(tr::now), - this, - [=] { psLinuxDelete(); }, + [] { SendKeySequence(Qt::Key_Delete); }, QKeySequence(Qt::ControlModifier | Qt::Key_Backspace)); edit->addSeparator(); psBold = edit->addAction( tr::lng_menu_formatting_bold(tr::now), - this, - [=] { psLinuxBold(); }, + [] { SendKeySequence(Qt::Key_B, Qt::ControlModifier); }, QKeySequence::Bold); psItalic = edit->addAction( tr::lng_menu_formatting_italic(tr::now), - this, - [=] { psLinuxItalic(); }, + [] { SendKeySequence(Qt::Key_I, Qt::ControlModifier); }, QKeySequence::Italic); psUnderline = edit->addAction( tr::lng_menu_formatting_underline(tr::now), - this, - [=] { psLinuxUnderline(); }, + [] { SendKeySequence(Qt::Key_U, Qt::ControlModifier); }, QKeySequence::Underline); psStrikeOut = edit->addAction( tr::lng_menu_formatting_strike_out(tr::now), - this, - [=] { psLinuxStrikeOut(); }, + [] { + SendKeySequence( + Qt::Key_X, + Qt::ControlModifier | Qt::ShiftModifier); + }, Ui::kStrikeOutSequence); psMonospace = edit->addAction( tr::lng_menu_formatting_monospace(tr::now), - this, - [=] { psLinuxMonospace(); }, + [] { + SendKeySequence( + Qt::Key_M, + Qt::ControlModifier | Qt::ShiftModifier); + }, Ui::kMonospaceSequence); psClearFormat = edit->addAction( tr::lng_menu_formatting_clear(tr::now), - this, - [=] { psLinuxClearFormat(); }, + [] { + SendKeySequence( + Qt::Key_N, + Qt::ControlModifier | Qt::ShiftModifier); + }, Ui::kClearFormatSequence); edit->addSeparator(); psSelectAll = edit->addAction( tr::lng_mac_menu_select_all(tr::now), - this, [=] { psLinuxSelectAll(); }, + [] { SendKeySequence(Qt::Key_A, Qt::ControlModifier); }, QKeySequence::SelectAll); edit->addSeparator(); @@ -1235,58 +1240,6 @@ void MainWindow::createGlobalMenu() { updateGlobalMenu(); } -void MainWindow::psLinuxUndo() { - SendKeySequence(Qt::Key_Z, Qt::ControlModifier); -} - -void MainWindow::psLinuxRedo() { - SendKeySequence(Qt::Key_Z, Qt::ControlModifier | Qt::ShiftModifier); -} - -void MainWindow::psLinuxCut() { - SendKeySequence(Qt::Key_X, Qt::ControlModifier); -} - -void MainWindow::psLinuxCopy() { - SendKeySequence(Qt::Key_C, Qt::ControlModifier); -} - -void MainWindow::psLinuxPaste() { - SendKeySequence(Qt::Key_V, Qt::ControlModifier); -} - -void MainWindow::psLinuxDelete() { - SendKeySequence(Qt::Key_Delete); -} - -void MainWindow::psLinuxSelectAll() { - SendKeySequence(Qt::Key_A, Qt::ControlModifier); -} - -void MainWindow::psLinuxBold() { - SendKeySequence(Qt::Key_B, Qt::ControlModifier); -} - -void MainWindow::psLinuxItalic() { - SendKeySequence(Qt::Key_I, Qt::ControlModifier); -} - -void MainWindow::psLinuxUnderline() { - SendKeySequence(Qt::Key_U, Qt::ControlModifier); -} - -void MainWindow::psLinuxStrikeOut() { - SendKeySequence(Qt::Key_X, Qt::ControlModifier | Qt::ShiftModifier); -} - -void MainWindow::psLinuxMonospace() { - SendKeySequence(Qt::Key_M, Qt::ControlModifier | Qt::ShiftModifier); -} - -void MainWindow::psLinuxClearFormat() { - SendKeySequence(Qt::Key_N, Qt::ControlModifier | Qt::ShiftModifier); -} - void MainWindow::updateGlobalMenuHook() { if (!positionInited()) { return; diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 6b675f8cf..d6f9839e0 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -79,21 +79,6 @@ private: void updateIconCounters(); void handleNativeSurfaceChanged(bool exist); - void psLinuxUndo(); - void psLinuxRedo(); - void psLinuxCut(); - void psLinuxCopy(); - void psLinuxPaste(); - void psLinuxDelete(); - void psLinuxSelectAll(); - - void psLinuxBold(); - void psLinuxItalic(); - void psLinuxUnderline(); - void psLinuxStrikeOut(); - void psLinuxMonospace(); - void psLinuxClearFormat(); - }; } // namespace Platform From 02ae5412468ded93b502000f37bf090fab0c9c96 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Thu, 25 Nov 2021 07:50:49 +0400 Subject: [PATCH 109/180] Get rid of unnecessary udev in docker image --- Telegram/build/docker/centos_env/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 40e152bb9..0bc4347ca 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -26,11 +26,11 @@ RUN yum -y install centos-release-scl && yum clean all RUN yum -y install git meson ninja-build autoconf automake libtool \ fontconfig-devel freetype-devel libX11-devel at-spi2-core-devel alsa-lib-devel \ - pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel libudev-devel \ - gtk3-devel pkgconfig bison yasm file which xorg-x11-util-macros \ + pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel gtk3-devel \ + perl-XML-Parser pkgconfig bison yasm file which xorg-x11-util-macros \ devtoolset-10-make devtoolset-10-gcc devtoolset-10-gcc-c++ \ devtoolset-10-binutils llvm-toolset-7.0 llvm-toolset-7.0-clang-devel \ - llvm-toolset-7.0-llvm-devel perl-XML-Parser && yum clean all + llvm-toolset-7.0-llvm-devel && yum clean all SHELL [ "bash", "-c", ". /opt/rh/devtoolset-10/enable; exec bash -c \"$@\"", "-s"] @@ -835,7 +835,7 @@ RUN git submodule init RUN git submodule update WORKDIR src/third_party/pipewire -RUN meson build +RUN meson build -Dspa-plugins=disabled WORKDIR ../../.. From f69d0823a9fdebacba70cdeb112f424b094a3666 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 20 Nov 2021 21:56:05 +0400 Subject: [PATCH 110/180] Get rid of oal-soft specific alext.h include --- .../media/audio/media_openal_functions.h | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/Telegram/SourceFiles/media/audio/media_openal_functions.h b/Telegram/SourceFiles/media/audio/media_openal_functions.h index 939ce982a..a11813623 100644 --- a/Telegram/SourceFiles/media/audio/media_openal_functions.h +++ b/Telegram/SourceFiles/media/audio/media_openal_functions.h @@ -7,48 +7,48 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include <alext.h> +#include <al.h> namespace OpenAL { /* Effect object functions */ -inline LPALGENEFFECTS alGenEffects; -inline LPALDELETEEFFECTS alDeleteEffects; -inline LPALISEFFECT alIsEffect; -inline LPALEFFECTI alEffecti; -inline LPALEFFECTIV alEffectiv; -inline LPALEFFECTF alEffectf; -inline LPALEFFECTFV alEffectfv; -inline LPALGETEFFECTI alGetEffecti; -inline LPALGETEFFECTIV alGetEffectiv; -inline LPALGETEFFECTF alGetEffectf; -inline LPALGETEFFECTFV alGetEffectfv; +inline void (AL_APIENTRY *alGenEffects)(ALsizei, ALuint*); +inline void (AL_APIENTRY *alDeleteEffects)(ALsizei, const ALuint*); +inline ALboolean (AL_APIENTRY *alIsEffect)(ALuint); +inline void (AL_APIENTRY *alEffecti)(ALuint, ALenum, ALint); +inline void (AL_APIENTRY *alEffectiv)(ALuint, ALenum, const ALint*); +inline void (AL_APIENTRY *alEffectf)(ALuint, ALenum, ALfloat); +inline void (AL_APIENTRY *alEffectfv)(ALuint, ALenum, const ALfloat*); +inline void (AL_APIENTRY *alGetEffecti)(ALuint, ALenum, ALint*); +inline void (AL_APIENTRY *alGetEffectiv)(ALuint, ALenum, ALint*); +inline void (AL_APIENTRY *alGetEffectf)(ALuint, ALenum, ALfloat*); +inline void (AL_APIENTRY *alGetEffectfv)(ALuint, ALenum, ALfloat*); /* Filter object functions */ -inline LPALGENFILTERS alGenFilters; -inline LPALDELETEFILTERS alDeleteFilters; -inline LPALISFILTER alIsFilter; -inline LPALFILTERI alFilteri; -inline LPALFILTERIV alFilteriv; -inline LPALFILTERF alFilterf; -inline LPALFILTERFV alFilterfv; -inline LPALGETFILTERI alGetFilteri; -inline LPALGETFILTERIV alGetFilteriv; -inline LPALGETFILTERF alGetFilterf; -inline LPALGETFILTERFV alGetFilterfv; +inline void (AL_APIENTRY *alGenFilters)(ALsizei, ALuint*); +inline void (AL_APIENTRY *alDeleteFilters)(ALsizei, const ALuint*); +inline ALboolean (AL_APIENTRY *alIsFilter)(ALuint); +inline void (AL_APIENTRY *alFilteri)(ALuint, ALenum, ALint); +inline void (AL_APIENTRY *alFilteriv)(ALuint, ALenum, const ALint*); +inline void (AL_APIENTRY *alFilterf)(ALuint, ALenum, ALfloat); +inline void (AL_APIENTRY *alFilterfv)(ALuint, ALenum, const ALfloat*); +inline void (AL_APIENTRY *alGetFilteri)(ALuint, ALenum, ALint*); +inline void (AL_APIENTRY *alGetFilteriv)(ALuint, ALenum, ALint*); +inline void (AL_APIENTRY *alGetFilterf)(ALuint, ALenum, ALfloat*); +inline void (AL_APIENTRY *alGetFilterfv)(ALuint, ALenum, ALfloat*); /* Auxiliary Effect Slot object functions */ -inline LPALGENAUXILIARYEFFECTSLOTS alGenAuxiliaryEffectSlots; -inline LPALDELETEAUXILIARYEFFECTSLOTS alDeleteAuxiliaryEffectSlots; -inline LPALISAUXILIARYEFFECTSLOT alIsAuxiliaryEffectSlot; -inline LPALAUXILIARYEFFECTSLOTI alAuxiliaryEffectSloti; -inline LPALAUXILIARYEFFECTSLOTIV alAuxiliaryEffectSlotiv; -inline LPALAUXILIARYEFFECTSLOTF alAuxiliaryEffectSlotf; -inline LPALAUXILIARYEFFECTSLOTFV alAuxiliaryEffectSlotfv; -inline LPALGETAUXILIARYEFFECTSLOTI alGetAuxiliaryEffectSloti; -inline LPALGETAUXILIARYEFFECTSLOTIV alGetAuxiliaryEffectSlotiv; -inline LPALGETAUXILIARYEFFECTSLOTF alGetAuxiliaryEffectSlotf; -inline LPALGETAUXILIARYEFFECTSLOTFV alGetAuxiliaryEffectSlotfv; +inline void (AL_APIENTRY *alGenAuxiliaryEffectSlots)(ALsizei, ALuint*); +inline void (AL_APIENTRY *alDeleteAuxiliaryEffectSlots)(ALsizei, const ALuint*); +inline ALboolean (AL_APIENTRY *alIsAuxiliaryEffectSlot)(ALuint); +inline void (AL_APIENTRY *alAuxiliaryEffectSloti)(ALuint, ALenum, ALint); +inline void (AL_APIENTRY *alAuxiliaryEffectSlotiv)(ALuint, ALenum, const ALint*); +inline void (AL_APIENTRY *alAuxiliaryEffectSlotf)(ALuint, ALenum, ALfloat); +inline void (AL_APIENTRY *alAuxiliaryEffectSlotfv)(ALuint, ALenum, const ALfloat*); +inline void (AL_APIENTRY *alGetAuxiliaryEffectSloti)(ALuint, ALenum, ALint*); +inline void (AL_APIENTRY *alGetAuxiliaryEffectSlotiv)(ALuint, ALenum, ALint*); +inline void (AL_APIENTRY *alGetAuxiliaryEffectSlotf)(ALuint, ALenum, ALfloat*); +inline void (AL_APIENTRY *alGetAuxiliaryEffectSlotfv)(ALuint, ALenum, ALfloat*); void LoadEFXExtension(); bool HasEFXExtension(); From 4e6334687743cdac1139bc6e41502f5f9f666cf5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sat, 20 Nov 2021 21:56:38 +0400 Subject: [PATCH 111/180] Use AL_REMIX_UNMATCHED_SOFT enum value instead of hard-coded value This makes the feature require version 1.21.0, previously was 1.20.1 --- Telegram/SourceFiles/media/audio/media_audio.cpp | 5 ++++- Telegram/lib_webrtc | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/media/audio/media_audio.cpp b/Telegram/SourceFiles/media/audio/media_audio.cpp index fa9ac0ddb..6a7205be6 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.cpp +++ b/Telegram/SourceFiles/media/audio/media_audio.cpp @@ -331,7 +331,10 @@ void Mixer::Track::createStream(AudioMsgId::Type type) { alSourcei(stream.source, AL_SOURCE_RELATIVE, 1); alSourcei(stream.source, AL_ROLLOFF_FACTOR, 0); if (alIsExtensionPresent("AL_SOFT_direct_channels_remix")) { - alSourcei(stream.source, alGetEnumValue("AL_DIRECT_CHANNELS_SOFT"), 2); + alSourcei( + stream.source, + alGetEnumValue("AL_DIRECT_CHANNELS_SOFT"), + alcGetEnumValue(nullptr, "AL_REMIX_UNMATCHED_SOFT")); } alGenBuffers(3, stream.buffers); if (speedEffect) { diff --git a/Telegram/lib_webrtc b/Telegram/lib_webrtc index 07f1f72e4..04cc1ff4a 160000 --- a/Telegram/lib_webrtc +++ b/Telegram/lib_webrtc @@ -1 +1 @@ -Subproject commit 07f1f72e45a75edb6304ba06c5e90fc287a1f0cb +Subproject commit 04cc1ff4a6fdade551e26441488ac0d0a208e96c From 57340f9514b66e42545b6f3e2bd2058b0a42764f Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 26 Nov 2021 23:06:39 +0400 Subject: [PATCH 112/180] Beta version 3.2.6. - Try out the new audio player with playlist shuffle and repeat. - Give a custom name to your desktop session to distinguish it in the sessions list. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/changelogs.cpp | 7 +++++++ Telegram/SourceFiles/core/version.h | 6 +++--- Telegram/build/version | 10 +++++----- changelog.txt | 5 +++++ 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 6b9c74065..f64be4c13 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="3.2.5.0" /> + Version="3.2.6.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index a30d9f00b..58780c793 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 3,2,5,0 - PRODUCTVERSION 3,2,5,0 + FILEVERSION 3,2,6,0 + PRODUCTVERSION 3,2,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "3.2.5.0" + VALUE "FileVersion", "3.2.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.5.0" + VALUE "ProductVersion", "3.2.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index abb519a32..471445acb 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 3,2,5,0 - PRODUCTVERSION 3,2,5,0 + FILEVERSION 3,2,6,0 + PRODUCTVERSION 3,2,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "3.2.5.0" + VALUE "FileVersion", "3.2.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.5.0" + VALUE "ProductVersion", "3.2.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index a0bfc8d7f..d2295cc42 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -125,6 +125,13 @@ std::map<int, const char*> BetaLogs() { "- Several crash fixes.\n" }, + { + 3002006, + "- Try out the new audio player with playlist shuffle and repeat.\n" + + "- Give a custom name to your desktop session " + "to distinguish it in the sessions list.\n" + } }; }; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index c17e229b9..3699ecdcf 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 3002005; -constexpr auto AppVersionStr = "3.2.5"; -constexpr auto AppBetaVersion = false; +constexpr auto AppVersion = 3002006; +constexpr auto AppVersionStr = "3.2.6"; +constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 8de5acb38..a58c44520 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 3002005 +AppVersion 3002006 AppVersionStrMajor 3.2 -AppVersionStrSmall 3.2.5 -AppVersionStr 3.2.5 -BetaChannel 0 +AppVersionStrSmall 3.2.6 +AppVersionStr 3.2.6 +BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 3.2.5 +AppVersionOriginal 3.2.6.beta diff --git a/changelog.txt b/changelog.txt index 698a2d8aa..fd6810eee 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +3.2.6 beta (26.11.21) + +- Try out the new audio player with playlist shuffle and repeat. +- Give a custom name to your desktop session to distinguish it in the sessions list. + 3.2.5 (16.11.21) [Windows, macOS] - Fix connecting in case bad characters appear in device name on Windows. From 9360f1f9707a54b6e110f8680f1fffed878b6b42 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 26 Nov 2021 23:51:47 +0400 Subject: [PATCH 113/180] Beta version 3.2.6: Fix build on macOS. --- .../SourceFiles/history/view/history_view_list_widget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index e0316f99f..78ee5c917 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1191,7 +1191,7 @@ bool ListWidget::hasCopyRestrictionForSelected() const { if (hasCopyRestriction()) { return true; } - for (const auto [itemId, selection] : _selected) { + for (const auto &[itemId, selection] : _selected) { if (const auto item = session().data().message(itemId)) { if (item->forbidsForward()) { return true; @@ -1202,7 +1202,7 @@ bool ListWidget::hasCopyRestrictionForSelected() const { } bool ListWidget::showCopyRestrictionForSelected() { - for (const auto [itemId, selection] : _selected) { + for (const auto &[itemId, selection] : _selected) { if (showCopyRestriction(session().data().message(itemId))) { return true; } From 6850cc5ab405154d6410ee1dde1db57a448393bb Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sat, 27 Nov 2021 10:20:59 +0400 Subject: [PATCH 114/180] Beta version 3.2.6: Fix build in Release on macOS. --- Telegram/SourceFiles/history/history_inner_widget.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 96e3607f7..658bbac75 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -279,7 +279,10 @@ void HistoryInner::setupSharingDisallowed() { const auto channel = _peer->asChannel(); _sharingDisallowed = chat ? Data::PeerFlagValue(chat, ChatDataFlag::NoForwards) - : Data::PeerFlagValue(channel, ChannelDataFlag::NoForwards); + : Data::PeerFlagValue( + channel, + ChannelDataFlag::NoForwards + ) | rpl::type_erased(); auto rights = chat ? chat->adminRightsValue() From feb1e9c4afec60fc458a8f8d372c31eeda6e5110 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sat, 27 Nov 2021 21:37:01 +0400 Subject: [PATCH 115/180] Beta version 3.2.6: Disable LTO in 64 bit Windows build. --- cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake b/cmake index 7332624b8..fdd7abd26 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 7332624b87233eee6d15d7da0e09d0287626d466 +Subproject commit fdd7abd2663d4f9f909372aa1dfb914101ce617c From 133f64f370403fac998b81f73ce8dcd05b2476ac Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sun, 28 Nov 2021 15:45:16 +0400 Subject: [PATCH 116/180] Fix disappearing emoji selector button. --- Telegram/SourceFiles/history/history_widget.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 8f437ef34..00f8abe5f 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4445,9 +4445,12 @@ void HistoryWidget::moveFieldControls() { void HistoryWidget::updateFieldSize() { auto kbShowShown = _history && !_kbShown && _keyboard->hasMarkup(); - auto fieldWidth = width() - _attachToggle->width() - st::historySendRight; - fieldWidth -= _send->width(); - fieldWidth -= _tabbedSelectorToggle->width(); + auto fieldWidth = width() + - _attachToggle->width() + - st::historySendRight + - _send->width() + - _tabbedSelectorToggle->width(); + if (_sendAs) fieldWidth -= _sendAs->width(); if (kbShowShown) fieldWidth -= _botKeyboardShow->width(); if (_cmdStartShown) fieldWidth -= _botCommandStart->width(); if (_silent) fieldWidth -= _silent->width(); From 27e80b8e42a6f2ab93a5e14d9aea92139d396ba4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sun, 28 Nov 2021 16:26:34 +0400 Subject: [PATCH 117/180] Fix crash in inconsistent local send as peer data. --- .../main/session/send_as_peers.cpp | 19 +++++++++++++++++++ .../SourceFiles/main/session/send_as_peers.h | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index 35f75c8e6..a3ef575a4 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -8,7 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/session/send_as_peers.h" #include "data/data_user.h" +#include "data/data_channel.h" #include "data/data_session.h" +#include "data/data_changes.h" #include "main/main_session.h" #include "apiwrap.h" @@ -22,6 +24,21 @@ constexpr auto kRequestEach = 30 * crl::time(1000); SendAsPeers::SendAsPeers(not_null<Session*> session) : _session(session) , _onlyMe({ session->user() }) { + _session->changes().peerUpdates( + Data::PeerUpdate::Flag::Rights + ) | rpl::map([=](const Data::PeerUpdate &update) { + const auto peer = update.peer; + const auto channel = peer->asChannel(); + return std::tuple( + peer, + peer->amAnonymous(), + channel ? channel->isPublic() : false); + }) | rpl::distinct_until_changed( + ) | rpl::filter([=](not_null<PeerData*> peer, bool, bool) { + return _lists.contains(peer); + }) | rpl::start_with_next([=](not_null<PeerData*> peer, bool, bool) { + refresh(peer); + }, _lifetime); } bool SendAsPeers::shouldChoose(not_null<PeerData*> peer) { @@ -96,6 +113,8 @@ not_null<PeerData*> SendAsPeers::ResolveChosen( const auto i = ranges::find(list, chosen, &PeerData::id); return (i != end(list)) ? (*i) + : !list.empty() + ? list.front() : (peer->isMegagroup() && peer->amAnonymous()) ? peer : peer->session().user(); diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h index 35a1db64e..49663db44 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.h +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -26,6 +26,8 @@ public: void saveChosen(not_null<PeerData*> peer, not_null<PeerData*> chosen); void setChosen(not_null<PeerData*> peer, PeerId chosenId); [[nodiscard]] PeerId chosen(not_null<PeerData*> peer) const; + + // If !list(peer).empty() then the result will be from that list. [[nodiscard]] not_null<PeerData*> resolveChosen( not_null<PeerData*> peer) const; @@ -48,6 +50,8 @@ private: rpl::event_stream<not_null<PeerData*>> _updates; + rpl::lifetime _lifetime; + }; } // namespace Main From 8460a625883e190e3a2dd54abc6274544ea5db42 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sun, 28 Nov 2021 16:37:09 +0400 Subject: [PATCH 118/180] Don't show "Send As" button when editing a message. Fixes #17302. --- .../SourceFiles/history/history_widget.cpp | 5 ++- .../history_view_compose_controls.cpp | 37 ++++++++++++------- .../controls/history_view_compose_controls.h | 2 +- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 00f8abe5f..9eec7f22a 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2270,6 +2270,9 @@ void HistoryWidget::registerDraftSource() { void HistoryWidget::setEditMsgId(MsgId msgId) { unregisterDraftSources(); _editMsgId = msgId; + if (_history) { + refreshSendAsToggle(); + } registerDraftSource(); } @@ -2388,7 +2391,7 @@ void HistoryWidget::setupSendAsToggle() { void HistoryWidget::refreshSendAsToggle() { Expects(_peer != nullptr); - if (!session().sendAsPeers().shouldChoose(_peer)) { + if (_editMsgId || !session().sendAsPeers().shouldChoose(_peer)) { _sendAs.destroy(); return; } else if (_sendAs) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 735b4dcc7..599a7cfe8 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1012,6 +1012,10 @@ void ComposeControls::init() { ) | rpl::start_with_next([=](const auto &id) { unregisterDraftSources(); updateSendButtonType(); + if (_history && updateSendAsButton()) { + updateControlsVisibility(); + updateControlsGeometry(_wrap->size()); + } registerDraftSource(); }, _wrap->lifetime()); @@ -1626,9 +1630,10 @@ void ComposeControls::initSendAsButton() { ) | rpl::filter([=](not_null<PeerData*> peer) { return _history && (peer == _history->peer); }) | rpl::start_with_next([=] { - updateSendAsButton(); - updateControlsVisibility(); - updateControlsGeometry(_wrap->size()); + if (updateSendAsButton()) { + updateControlsVisibility(); + updateControlsGeometry(_wrap->size()); + } }, _wrap->lifetime()); } @@ -1942,21 +1947,27 @@ void ComposeControls::updateMessagesTTLShown() { } } -void ComposeControls::updateSendAsButton() { +bool ComposeControls::updateSendAsButton() { Expects(_history != nullptr); const auto peer = _history->peer; - if (!session().sendAsPeers().shouldChoose(peer)) { + if (isEditingMessage() || !session().sendAsPeers().shouldChoose(peer)) { + if (!_sendAs) { + return false; + } _sendAs = nullptr; - } else if (!_sendAs) { - _sendAs = std::make_unique<Ui::SendAsButton>( - _wrap.get(), - st::sendAsButton); - Ui::SetupSendAsButton( - _sendAs.get(), - rpl::single(peer.get()), - _window); + return true; + } else if (_sendAs) { + return false; } + _sendAs = std::make_unique<Ui::SendAsButton>( + _wrap.get(), + st::sendAsButton); + Ui::SetupSendAsButton( + _sendAs.get(), + rpl::single(peer.get()), + _window); + return true; } void ComposeControls::paintBackground(QRect clip) { diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 09b498b48..1a19acca9 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -206,7 +206,7 @@ private: void updateSubmitSettings(); void updateSendButtonType(); void updateMessagesTTLShown(); - void updateSendAsButton(); + bool updateSendAsButton(); void updateHeight(); void updateWrappingVisibility(); void updateControlsVisibility(); From f2915064b553478c30e385e037374618fa20d1c1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 09:24:38 +0400 Subject: [PATCH 119/180] Fix "Send As" list refresh on admin anonymity change. --- Telegram/SourceFiles/main/session/send_as_peers.cpp | 6 +++--- Telegram/SourceFiles/main/session/send_as_peers.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index a3ef575a4..4d1532318 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -37,7 +37,7 @@ SendAsPeers::SendAsPeers(not_null<Session*> session) ) | rpl::filter([=](not_null<PeerData*> peer, bool, bool) { return _lists.contains(peer); }) | rpl::start_with_next([=](not_null<PeerData*> peer, bool, bool) { - refresh(peer); + refresh(peer, true); }, _lifetime); } @@ -46,14 +46,14 @@ bool SendAsPeers::shouldChoose(not_null<PeerData*> peer) { return peer->canWrite() && (list(peer).size() > 1); } -void SendAsPeers::refresh(not_null<PeerData*> peer) { +void SendAsPeers::refresh(not_null<PeerData*> peer, bool force) { if (!peer->isMegagroup()) { return; } const auto now = crl::now(); const auto i = _lastRequestTime.find(peer); const auto when = (i == end(_lastRequestTime)) ? -1 : i->second; - if (when >= 0 && now < when + kRequestEach) { + if (!force && (when >= 0 && now < when + kRequestEach)) { return; } _lastRequestTime[peer] = now; diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h index 49663db44..302bdf02d 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.h +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -18,7 +18,7 @@ public: explicit SendAsPeers(not_null<Session*> session); bool shouldChoose(not_null<PeerData*> peer); - void refresh(not_null<PeerData*> peer); + void refresh(not_null<PeerData*> peer, bool force = false); [[nodiscard]] const std::vector<not_null<PeerData*>> &list( not_null<PeerData*> peer) const; [[nodiscard]] rpl::producer<not_null<PeerData*>> updated() const; From abc40f7e43d436b9f81aa002917b911d4abecf36 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sat, 27 Nov 2021 10:21:45 +0400 Subject: [PATCH 120/180] Beta version 3.2.6: Add logs for scroll on Linux. --- Telegram/build/docker/centos_env/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 0bc4347ca..d71334701 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -46,7 +46,7 @@ RUN ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake RUN rm $CMAKE_FILE FROM builder AS patches -RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout be978cc050 +RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout dfe3991130 FROM builder AS extra-cmake-modules From 5afcd47ab0c51a2aabf8d7f1c97204fddc3a14c5 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Sat, 27 Nov 2021 10:29:47 +0400 Subject: [PATCH 121/180] Beta version 3.2.6: Update tg_owt revision in Dockerfile. --- Telegram/build/docker/centos_env/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index d71334701..b4f4cb4f6 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -829,7 +829,7 @@ RUN mkdir tg_owt WORKDIR tg_owt RUN git init RUN git remote add origin $GIT/desktop-app/tg_owt.git -RUN git fetch --depth=1 origin d578c760dc6f1ae5f0f3bb5317b0b2ed04b79138 +RUN git fetch --depth=1 origin b02478677baac6d563589f216800ff9cea0fd65c RUN git reset --hard FETCH_HEAD RUN git submodule init RUN git submodule update From 453ce1bff94b4ac37c9178fc37e0e544c42a7a61 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 09:47:39 +0400 Subject: [PATCH 122/180] Beta version 3.2.6: Fix build on Linux. --- Telegram/SourceFiles/boxes/peers/add_participants_box.cpp | 1 - Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp | 2 -- Telegram/SourceFiles/boxes/sessions_box.cpp | 2 -- Telegram/ThirdParty/tgcalls | 2 +- Telegram/cmake/lib_tgcalls.cmake | 6 +----- 5 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index edaa07d05..01dbff4af 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -465,7 +465,6 @@ void AddSpecialBoxController::loadMoreRows() { MTP_long(participantsHash) )).done([=](const MTPchannels_ChannelParticipants &result) { _loadRequestId = 0; - auto &session = channel->session(); result.match([&](const MTPDchannels_channelParticipants &data) { const auto &[availableCount, list] = Api::ChatParticipants::Parse( channel, diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index 09b67a480..bc9b9508e 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -535,7 +535,6 @@ void ParticipantsAdditionalData::applyAdminLocally( UserData *user, ChatAdminRightsInfo rights, const QString &rank) { - const auto date = base::unixtime::now(); // Incorrect, but ignored. if (isCreator(user) && user->isSelf()) { applyParticipant(Api::ChatParticipant( Api::ChatParticipant::Type::Creator, @@ -571,7 +570,6 @@ void ParticipantsAdditionalData::applyBannedLocally( not_null<PeerData*> participant, ChatRestrictionsInfo rights) { const auto user = participant->asUser(); - const auto date = base::unixtime::now(); // Incorrect, but ignored. if (!rights.flags) { if (user) { applyParticipant(Api::ChatParticipant( diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index d398dac53..1d4d01395 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -699,8 +699,6 @@ void SessionsContent::List::paintEvent(QPaintEvent *e) { const auto x = st::sessionPadding.left(); const auto y = st::sessionPadding.top(); const auto w = width(); - const auto xact = st::sessionTerminateSkip - + st::sessionTerminate.iconPosition.x(); p.translate(0, from * st::sessionHeight); for (auto i = from; i != till; ++i) { const auto &entry = _items[i]; diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index 0e3f3705e..6dbefa5bf 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit 0e3f3705ecce37dc83ede0b04180a8cb0830aec3 +Subproject commit 6dbefa5bfc0fdf07eb42af103d8af0f481511c2a diff --git a/Telegram/cmake/lib_tgcalls.cmake b/Telegram/cmake/lib_tgcalls.cmake index bfb962d6e..722790bca 100644 --- a/Telegram/cmake/lib_tgcalls.cmake +++ b/Telegram/cmake/lib_tgcalls.cmake @@ -105,8 +105,6 @@ PRIVATE platform/darwin/DarwinInterface.mm platform/darwin/DarwinVideoSource.h platform/darwin/DarwinVideoSource.mm - platform/darwin/DesktopCaptureSourceView.h - platform/darwin/DesktopCaptureSourceView.mm platform/darwin/DesktopSharingCapturer.h platform/darwin/DesktopSharingCapturer.mm platform/darwin/GLVideoView.h @@ -147,7 +145,7 @@ PRIVATE platform/darwin/VideoMetalView.mm platform/darwin/VideoMetalViewMac.h platform/darwin/VideoMetalViewMac.mm - + # POSIX # Teleram Desktop @@ -187,8 +185,6 @@ elseif (APPLE) -fobjc-arc ) remove_target_sources(lib_tgcalls ${tgcalls_loc} - platform/darwin/DesktopCaptureSourceView.h - platform/darwin/DesktopCaptureSourceView.mm platform/darwin/GLVideoView.h platform/darwin/GLVideoView.mm platform/darwin/GLVideoViewMac.h From 07dfe88d62a738507e133ebb5ecc52fe1e88f863 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 10:25:30 +0400 Subject: [PATCH 123/180] Fix player controls with animations disabled. --- Telegram/SourceFiles/media/player/media_player.style | 2 +- Telegram/SourceFiles/media/player/media_player_widget.cpp | 7 ++++--- Telegram/SourceFiles/media/player/media_player_widget.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index e7f50eb28..6c40005c5 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -291,7 +291,7 @@ mediaPlayerFileLayout: OverviewFileLayout(overviewFileLayout) { mediaPlayerFloatSize: 128px; mediaPlayerFloatMargin: 12px; -mediaPlayerMenuPosition: point(-2px, 0px); +mediaPlayerMenuPosition: point(-2px, -2px); mediaPlayerOrderMenu: Menu(defaultMenu) { itemIconPosition: point(13px, 8px); itemPadding: margins(49px, 9px, 17px, 11px); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 33aed5f7c..43ac0a60c 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -528,17 +528,18 @@ Widget::Widget( hidePlaylistOn(_playPause); hidePlaylistOn(_close); + hidePlaylistOn(_rightControls); setType(AudioMsgId::Type::Song); } -void Widget::hidePlaylistOn(const object_ptr<Ui::IconButton> &button) { - button->events( +void Widget::hidePlaylistOn(not_null<Ui::RpWidget*> widget) { + widget->events( ) | rpl::filter([=](not_null<QEvent*> e) { return (e->type() == QEvent::Enter); }) | rpl::start_with_next([=] { updateOverLabelsState(false); - }, button->lifetime()); + }, widget->lifetime()); } void Widget::setupRightControls() { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index ebe6a367e..bd8debcbe 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -84,7 +84,7 @@ private: [[nodiscard]] int getTimeRight() const; void updateOverLabelsState(QPoint pos); void updateOverLabelsState(bool over); - void hidePlaylistOn(const object_ptr<Ui::IconButton> &button); + void hidePlaylistOn(not_null<Ui::RpWidget*> widget); void updatePlayPrevNextPositions(); void updateLabelsGeometry(); From c153cdc70e350b2f4510d93a8bb6e8111f4b4a4a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 11:11:16 +0400 Subject: [PATCH 124/180] Change volume by wheel events on volume icon. --- .../media/player/media_player_volume_controller.cpp | 13 ++++++++++++- .../media/player/media_player_volume_controller.h | 6 +++++- .../media/player/media_player_widget.cpp | 7 ++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index 37d058e8b..53d207a28 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -57,6 +57,10 @@ void VolumeController::setIsVertical(bool vertical) { _slider->setAlwaysDisplayMarker(vertical); } +void VolumeController::outerWheelEvent(not_null<QWheelEvent*> e) { + QGuiApplication::sendEvent(_slider.data(), e); +} + void VolumeController::resizeEvent(QResizeEvent *e) { _slider->setGeometry(rect()); } @@ -78,7 +82,8 @@ void VolumeController::applyVolumeChange(float64 volume) { void PrepareVolumeDropdown( not_null<Dropdown*> dropdown, - not_null<Window::SessionController*> controller) { + not_null<Window::SessionController*> controller, + rpl::producer<not_null<QWheelEvent*>> outerWheelEvents) { const auto volume = Ui::CreateChild<VolumeController>( dropdown.get(), controller); @@ -98,6 +103,12 @@ void PrepareVolumeDropdown( - ((st::mediaPlayerVolumeSize.width() - st::mediaPlayerPanelPlayback.width) / 2))); }, volume->lifetime()); + + std::move( + outerWheelEvents + ) | rpl::start_with_next([=](not_null<QWheelEvent*> e) { + volume->outerWheelEvent(e); + }, volume->lifetime()); } } // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.h b/Telegram/SourceFiles/media/player/media_player_volume_controller.h index 05a9da23b..49b4c8cb4 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.h +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.h @@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "base/object_ptr.h" +class QWheelEvent; + namespace Ui { class MediaSlider; } // namespace Ui @@ -29,6 +31,7 @@ public: not_null<Window::SessionController*> controller); void setIsVertical(bool vertical); + void outerWheelEvent(not_null<QWheelEvent*> e); protected: void resizeEvent(QResizeEvent *e) override; @@ -43,6 +46,7 @@ private: void PrepareVolumeDropdown( not_null<Dropdown*> dropdown, - not_null<Window::SessionController*> controller); + not_null<Window::SessionController*> controller, + rpl::producer<not_null<QWheelEvent*>> outerWheelEvents); } // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 43ac0a60c..fbebd36c5 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -515,7 +515,12 @@ Widget::Widget( handleSongUpdate(state); }, lifetime()); - PrepareVolumeDropdown(_volume.get(), controller); + PrepareVolumeDropdown(_volume.get(), controller, _volumeToggle->events( + ) | rpl::filter([=](not_null<QEvent*> e) { + return (e->type() == QEvent::Wheel); + }) | rpl::map([=](not_null<QEvent*> e) { + return not_null{ static_cast<QWheelEvent*>(e.get()) }; + })); _volumeToggle->installEventFilter(_volume.get()); _volume->events( ) | rpl::start_with_next([=](not_null<QEvent*> e) { From 3c0f80719eb5676a40287fc1bce66872771a5645 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 11:11:38 +0400 Subject: [PATCH 125/180] Don't show playlist with mouse over controls. --- Telegram/SourceFiles/media/player/media_player_widget.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index fbebd36c5..a96159f42 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -741,7 +741,6 @@ void Widget::markOver(bool over) { _over = true; _wontBeOver = false; updateControlsWrapVisibility(); - updateOverLabelsState(true); } else { _wontBeOver = true; InvokeQueued(this, [=] { From 16232c0a4a4eb36055274b7f8b707c13b60fab39 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 14:54:31 +0400 Subject: [PATCH 126/180] Open channel profile from message sender click. --- .../SourceFiles/core/click_handler_types.h | 2 + Telegram/SourceFiles/data/data_peer.cpp | 1 + .../admin_log/history_admin_log_inner.cpp | 11 ++++-- .../history/view/history_view_element.cpp | 38 +++++++++++++++++-- .../history/view/history_view_element.h | 1 + Telegram/lib_ui | 2 +- 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index b68d212e4..6bdb282f9 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/basic_click_handlers.h" +constexpr auto kPeerLinkPeerIdProperty = 0x01; + namespace Main { class Session; } // namespace Main diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index c8abcbb6e..a18057ca0 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -163,6 +163,7 @@ bool UpdateBotCommands( PeerClickHandler::PeerClickHandler(not_null<PeerData*> peer) : _peer(peer) { + setProperty(kPeerLinkPeerIdProperty, peer->id.value); } void PeerClickHandler::onClick(ClickContext context) const { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 3ecd53f4e..8b773e73c 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -1156,10 +1156,11 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { : App::hoveredLinkItem(); auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(link.get()); auto lnkDocument = dynamic_cast<DocumentClickHandler*>(link.get()); - auto lnkPeer = dynamic_cast<PeerClickHandler*>(link.get()); auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false; auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false; auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false; + const auto fromId = PeerId( + link->property(kPeerLinkPeerIdProperty).toULongLong()); if (lnkPhoto || lnkDocument) { if (isUponSelected > 0) { _menu->addAction(tr::lng_context_copy_selected(tr::now), [=] { @@ -1231,9 +1232,11 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } } - } else if (lnkPeer) { // suggest to block - if (auto user = lnkPeer->peer()->asUser()) { - suggestRestrictUser(user); + } else if (fromId) { // suggest to block + if (const auto userId = peerToUser(fromId)) { + if (const auto user = session().data().user(userId)) { + suggestRestrictUser(user); + } } } else { // maybe cursor on some text history item? const auto item = view ? view->data().get() : nullptr; diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 2e2c2412a..3fe06f426 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -19,7 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "core/application.h" #include "core/core_settings.h" +#include "core/click_handler_types.h" #include "main/main_session.h" +#include "main/main_domain.h" #include "chat_helpers/stickers_emoji_pack.h" #include "window/window_session_controller.h" #include "ui/effects/path_shift_gradient.h" @@ -572,10 +574,37 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) { } ClickHandlerPtr Element::fromLink() const { + if (_fromLink) { + return _fromLink; + } const auto item = data(); - const auto from = item->displayFrom(); - if (from) { - return from->openLink(); + if (const auto from = item->displayFrom()) { + _fromLink = std::make_shared<LambdaClickHandler>([=]( + ClickContext context) { + if (context.button != Qt::LeftButton) { + return; + } + const auto my = context.other.value<ClickHandlerContext>(); + const auto window = [&]() -> Window::SessionController* { + if (const auto controller = my.sessionWindow.get()) { + return controller; + } + const auto session = &from->session(); + const auto &windows = session->windows(); + if (windows.empty()) { + session->domain().activate(&session->account()); + if (windows.empty()) { + return nullptr; + } + } + return windows.front(); + }(); + if (window) { + window->showPeerInfo(from); + } + }); + _fromLink->setProperty(kPeerLinkPeerIdProperty, from->id.value); + return _fromLink; } if (const auto forwarded = item->Get<HistoryMessageForwarded>()) { if (forwarded->imported) { @@ -590,7 +619,8 @@ ClickHandlerPtr Element::fromLink() const { static const auto hidden = std::make_shared<LambdaClickHandler>([] { Ui::Toast::Show(tr::lng_forwarded_hidden(tr::now)); }); - return hidden; + _fromLink = hidden; + return _fromLink; } void Element::createUnreadBar(rpl::producer<QString> text) { diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 22cbdeebc..1522ac402 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -421,6 +421,7 @@ private: const not_null<ElementDelegate*> _delegate; const not_null<HistoryItem*> _data; std::unique_ptr<Media> _media; + mutable ClickHandlerPtr _fromLink; bool _isScheduledUntilOnline = false; const QDateTime _dateTime; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 725654654..5cf6981bb 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 725654654d44554c7ce2ebd86d242f7c46b13591 +Subproject commit 5cf6981bbbc7098fab90d7a421e4599ec0013115 From fc4cdd4482f1a5bd91c7c11e63a1852104b89910 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 15:24:08 +0400 Subject: [PATCH 127/180] Fix crash in legacy calls with new WebRTC. --- Telegram/ThirdParty/libtgvoip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/ThirdParty/libtgvoip b/Telegram/ThirdParty/libtgvoip index 7efc43006..2cffda622 160000 --- a/Telegram/ThirdParty/libtgvoip +++ b/Telegram/ThirdParty/libtgvoip @@ -1 +1 @@ -Subproject commit 7efc430061aa37945fde7325439236526084c598 +Subproject commit 2cffda6222f07cd7d0aa4627a06fa99b05a3956d From 03775e0cc5ce108a0ebf7c1767b9f0cbfe70ecb2 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 15:24:30 +0400 Subject: [PATCH 128/180] Allow to ban channels in groups from moderate actions. --- .../admin_log/history_admin_log_inner.cpp | 67 ++++++++++++------- .../admin_log/history_admin_log_inner.h | 11 ++- Telegram/SourceFiles/history/history_item.cpp | 5 +- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 8b773e73c..6041feaa9 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_cursor_state.h" #include "chat_helpers/message_field.h" #include "boxes/sticker_set_box.h" +#include "ui/boxes/confirm_box.h" #include "base/platform/base_platform_info.h" #include "base/unixtime.h" #include "mainwindow.h" @@ -1233,10 +1234,8 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } } else if (fromId) { // suggest to block - if (const auto userId = peerToUser(fromId)) { - if (const auto user = session().data().user(userId)) { - suggestRestrictUser(user); - } + if (const auto participant = session().data().peer(fromId)) { + suggestRestrictParticipant(participant); } } else { // maybe cursor on some text history item? const auto item = view ? view->data().get() : nullptr; @@ -1360,38 +1359,57 @@ void InnerWidget::copyContextText(FullMsgId itemId) { } } -void InnerWidget::suggestRestrictUser(not_null<UserData*> user) { +void InnerWidget::suggestRestrictParticipant( + not_null<PeerData*> participant) { Expects(_menu != nullptr); - if (!_channel->isMegagroup() || !_channel->canBanMembers() || _admins.empty()) { + if (!_channel->isMegagroup() + || !_channel->canBanMembers() + || _admins.empty()) { return; } - if (base::contains(_admins, user)) { - if (!base::contains(_adminsCanEdit, user)) { + if (ranges::contains(_admins, participant)) { + if (!ranges::contains(_adminsCanEdit, participant)) { return; } } _menu->addAction(tr::lng_context_restrict_user(tr::now), [=] { + const auto user = participant->asUser(); auto editRestrictions = [=](bool hasAdminRights, ChatRestrictionsInfo currentRights) { auto weak = QPointer<InnerWidget>(this); - auto weakBox = std::make_shared<QPointer<EditRestrictedBox>>(); + auto weakBox = std::make_shared<QPointer<Ui::BoxContent>>(); auto box = Box<EditRestrictedBox>(_channel, user, hasAdminRights, currentRights); box->setSaveCallback([=]( ChatRestrictionsInfo oldRights, ChatRestrictionsInfo newRights) { if (weak) { - weak->restrictUser(user, oldRights, newRights); + weak->restrictParticipant(participant, oldRights, newRights); } if (*weakBox) { (*weakBox)->closeBox(); } }); - *weakBox = QPointer<EditRestrictedBox>(box.data()); - _controller->show( - std::move(box), - Ui::LayerOption::KeepOther); + *weakBox = _controller->show(std::move(box)); }; - if (base::contains(_admins, user)) { + if (!user) { + const auto text = (_channel->isBroadcast() + ? tr::lng_profile_sure_kick_channel + : tr::lng_profile_sure_kick)( + tr::now, + lt_user, + participant->name); + auto weakBox = std::make_shared<QPointer<Ui::BoxContent>>(); + const auto sure = crl::guard(this, [=] { + restrictParticipant( + participant, + ChatRestrictionsInfo(), + ChannelData::KickedRestrictedRights(participant)); + if (*weakBox) { + (*weakBox)->closeBox(); + } + }); + *weakBox = _controller->show(Box<Ui::ConfirmBox>(text, sure)); + } else if (base::contains(_admins, user)) { editRestrictions(true, ChatRestrictionsInfo()); } else { _api.request(MTPchannels_GetParticipant( @@ -1420,30 +1438,33 @@ void InnerWidget::suggestRestrictUser(not_null<UserData*> user) { }); } -void InnerWidget::restrictUser( - not_null<UserData*> user, +void InnerWidget::restrictParticipant( + not_null<PeerData*> participant, ChatRestrictionsInfo oldRights, ChatRestrictionsInfo newRights) { const auto done = [=](ChatRestrictionsInfo newRights) { - restrictUserDone(user, newRights); + restrictParticipantDone(participant, newRights); }; const auto callback = SaveRestrictedCallback( _channel, - user, + participant, crl::guard(this, done), nullptr); callback(oldRights, newRights); } -void InnerWidget::restrictUserDone( - not_null<UserData*> user, +void InnerWidget::restrictParticipantDone( + not_null<PeerData*> participant, ChatRestrictionsInfo rights) { if (rights.flags) { _admins.erase( - std::remove(_admins.begin(), _admins.end(), user), + std::remove(_admins.begin(), _admins.end(), participant), _admins.end()); _adminsCanEdit.erase( - std::remove(_adminsCanEdit.begin(), _adminsCanEdit.end(), user), + std::remove( + _adminsCanEdit.begin(), + _adminsCanEdit.end(), + participant), _adminsCanEdit.end()); } _downLoaded = false; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index 0f3d1d959..65792b3d4 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -204,9 +204,14 @@ private: void copyContextText(FullMsgId itemId); void copySelectedText(); TextForMimeData getSelectedText() const; - void suggestRestrictUser(not_null<UserData*> user); - void restrictUser(not_null<UserData*> user, ChatRestrictionsInfo oldRights, ChatRestrictionsInfo newRights); - void restrictUserDone(not_null<UserData*> user, ChatRestrictionsInfo rights); + void suggestRestrictParticipant(not_null<PeerData*> participant); + void restrictParticipant( + not_null<PeerData*> participant, + ChatRestrictionsInfo oldRights, + ChatRestrictionsInfo newRights); + void restrictParticipantDone( + not_null<PeerData*> participant, + ChatRestrictionsInfo rights); void requestAdmins(); void checkPreloadMore(); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 93ab1cd46..613ba2b99 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -727,10 +727,7 @@ bool HistoryItem::suggestReport() const { bool HistoryItem::suggestBanReport() const { const auto channel = history()->peer->asChannel(); - const auto fromUser = from()->asUser(); - if (!channel - || !fromUser - || !channel->canRestrictParticipant(fromUser)) { + if (!channel || !channel->canRestrictParticipant(from())) { return false; } return !isPost() && !out(); From 2eb64e051ba2ae05d9177fd29b18c771df7916e4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 15:56:50 +0400 Subject: [PATCH 129/180] Don't start animations from QEvent::Enter. The enter/leave events may be sent from ~QWidget() and if we start animating something with grabbing of widgets it crashes sometimes. --- .../media/player/media_player_widget.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index a96159f42..5371b8509 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -68,6 +68,7 @@ private: const Fn<void(bool)> _menuOverCallback; base::unique_qptr<Ui::DropdownMenu> _menu; bool _temporarilyHidden = false; + bool _overButton = false; }; @@ -120,9 +121,17 @@ WithDropdownController::WithDropdownController( , _menuOverCallback(std::move(menuOverCallback)) { button->events( ) | rpl::filter([=](not_null<QEvent*> e) { - return (e->type() == QEvent::Enter); - }) | rpl::start_with_next([=] { - showMenu(); + return (e->type() == QEvent::Enter) + || (e->type() == QEvent::Leave); + }) | rpl::start_with_next([=](not_null<QEvent*> e) { + _overButton = (e->type() == QEvent::Enter); + if (_overButton) { + InvokeQueued(button, [=] { + if (_overButton) { + showMenu(); + } + }); + } }, button->lifetime()); } @@ -740,7 +749,9 @@ void Widget::markOver(bool over) { if (over) { _over = true; _wontBeOver = false; - updateControlsWrapVisibility(); + InvokeQueued(this, [=] { + updateControlsWrapVisibility(); + }); } else { _wontBeOver = true; InvokeQueued(this, [=] { From 09bed4989bbe0fc0b22b3f1d8561c059c02fd9f1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 15:59:48 +0400 Subject: [PATCH 130/180] Cherry-pick a crash-fix patch for Qt 6.2. --- Telegram/build/docker/centos_env/Dockerfile | 2 +- Telegram/build/prepare/prepare.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index b4f4cb4f6..9fa12dd77 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -46,7 +46,7 @@ RUN ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake RUN rm $CMAKE_FILE FROM builder AS patches -RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout dfe3991130 +RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout 4c21dfa0db FROM builder AS extra-cmake-modules diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 7158f0fd6..e368456fb 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -401,7 +401,7 @@ if customRunCommand: stage('patches', """ git clone https://github.com/desktop-app/patches.git cd patches - git checkout eb843853be + git checkout 4c21dfa0db """) stage('depot_tools', """ From bd0e4c00758e771fbb60b6abfcb4753c771e19b4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 16:21:07 +0400 Subject: [PATCH 131/180] Backport fix for empty bus name to glibmm patch. --- Telegram/build/docker/centos_env/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 9fa12dd77..0bc0bf4f8 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -46,7 +46,7 @@ RUN ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake RUN rm $CMAKE_FILE FROM builder AS patches -RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout 4c21dfa0db +RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout 19e5e028c7 FROM builder AS extra-cmake-modules From 2ad20d6c4a6036be70b5d1e73a7e0493b9b05d27 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sun, 28 Nov 2021 23:15:51 +0400 Subject: [PATCH 132/180] Instantiate a local dbus server for webview IPC --- .../platform/linux/launcher_linux.cpp | 16 +++------------- .../platform/linux/specific_linux.cpp | 10 ++++++++-- Telegram/lib_webview | 2 +- snap/snapcraft.yaml | 4 ---- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp index 7994350ea..931cbccd1 100644 --- a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp @@ -13,11 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <QtWidgets/QApplication> -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION -#include <glibmm.h> -#include <giomm.h> -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION - #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> @@ -56,15 +51,10 @@ Launcher::Launcher(int argc, char *argv[]) } int Launcher::exec() { -#ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION - Glib::init(); - Gio::init(); -#endif // !DESKTOP_APP_DISABLE_DBUS_INTEGRATION - for (auto i = begin(_arguments), e = end(_arguments); i != e; ++i) { - if (*i == "-webviewhelper" && std::distance(i, e) > 2) { - Webview::WebKit2Gtk::SetServiceName(*(i + 2)); - return Webview::WebKit2Gtk::Exec(*(i + 1)); + if (*i == "-webviewhelper" && std::distance(i, e) > 1) { + Webview::WebKit2Gtk::SetSocketPath(*(i + 1)); + return Webview::WebKit2Gtk::Exec(); } } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 325e464d0..b0e90e454 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -709,6 +709,9 @@ void start() { qputenv("PULSE_PROP_application.icon_name", GetIconName().toLatin1()); #ifndef DESKTOP_APP_DISABLE_DBUS_INTEGRATION + Glib::init(); + Gio::init(); + Glib::set_prgname(cExeName().toStdString()); Glib::set_application_name(std::string(AppName)); @@ -742,8 +745,11 @@ void start() { char h[33] = { 0 }; hashMd5Hex(d.constData(), d.size(), h); - Webview::WebKit2Gtk::SetServiceName( - kWebviewService.utf16().arg(h).arg("%1").toStdString()); + Webview::WebKit2Gtk::SetSocketPath(qsl("%1/%2-%3-webview-%4").arg( + QDir::tempPath(), + h, + cGUIDStr(), + qsl("%1")).toStdString()); } void finish() { diff --git a/Telegram/lib_webview b/Telegram/lib_webview index f82ad9d60..754657aed 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit f82ad9d608000171ef3822713712346f38aee50d +Subproject commit 754657aededfaa646bc6e8f48a18141982762785 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index f4f0065d8..a65d75ead 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -68,10 +68,6 @@ plugs: default-provider: gtk-common-themes slots: - tdesktop-dbus: - interface: dbus - bus: session - name: org.telegram.desktop tdesktop-mpris: interface: mpris name: tdesktop From d4c6475ae8f6668d1f5676d15a3c3196dc42c4ce Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 17:29:45 +0400 Subject: [PATCH 133/180] Show sessions list in a Settings Section. --- Telegram/SourceFiles/boxes/sessions_box.cpp | 3 ++- Telegram/SourceFiles/core/local_url_handlers.cpp | 9 ++++----- Telegram/SourceFiles/info/info_top_bar.cpp | 2 ++ Telegram/SourceFiles/settings/settings_common.cpp | 3 +++ Telegram/SourceFiles/settings/settings_common.h | 1 + .../settings/settings_privacy_security.cpp | 13 ++++++++++--- .../settings/settings_privacy_security.h | 4 ++++ 7 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 1d4d01395..5ede4940d 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -129,7 +129,7 @@ void SessionInfoBox( [[nodiscard]] QString LocationAndDate(const EntryData &entry) { return (entry.location.isEmpty() ? entry.ip : entry.location) + (entry.hash - ? (QString::fromUtf8(" \xe2\x80\x93 ") + entry.active) + ? (QString::fromUtf8(" \xE2\x80\xA2 ") + entry.active) : QString()); } @@ -768,6 +768,7 @@ Sessions::Sessions( void Sessions::setupContent(not_null<Window::SessionController*> controller) { const auto container = Ui::CreateChild<Ui::VerticalLayout>(this); + AddSkip(container, st::settingsPrivacySkip); const auto content = container->add( object_ptr<SessionsContent>(container, controller)); content->setupContent(); diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 59d8c1bfb..0a3671611 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -376,17 +376,16 @@ bool ResolveSettings( if (section.isEmpty()) { controller->window().showSettings(); return true; - } - if (section == qstr("devices")) { - controller->session().api().authorizations().reload(); - controller->show(Box<SessionsBox>(controller)); - return true; } else if (section == qstr("language")) { ShowLanguagesBox(); return true; + } else if (section == qstr("devices")) { + controller->session().api().authorizations().reload(); } const auto type = (section == qstr("folders")) ? ::Settings::Type::Folders + : (section == qstr("devices")) + ? ::Settings::Type::Sessions : ::Settings::Type::Main; controller->showSettings(type); return true; diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index fae562b0c..f7e01b807 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -617,6 +617,8 @@ rpl::producer<QString> TitleValue( return tr::lng_settings_section_notify(); case Section::SettingsType::PrivacySecurity: return tr::lng_settings_section_privacy(); + case Section::SettingsType::Sessions: + return tr::lng_settings_sessions_title(); case Section::SettingsType::Advanced: return tr::lng_settings_advanced(); case Section::SettingsType::Chat: diff --git a/Telegram/SourceFiles/settings/settings_common.cpp b/Telegram/SourceFiles/settings/settings_common.cpp index 7b9e48c55..5bfa90bae 100644 --- a/Telegram/SourceFiles/settings/settings_common.cpp +++ b/Telegram/SourceFiles/settings/settings_common.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/box_content_divider.h" #include "ui/widgets/buttons.h" #include "boxes/abstract_box.h" +#include "boxes/sessions_box.h" #include "window/themes/window_theme_editor_box.h" #include "window/window_session_controller.h" #include "window/window_controller.h" @@ -47,6 +48,8 @@ object_ptr<Section> CreateSection( return object_ptr<Notifications>(parent, controller); case Type::PrivacySecurity: return object_ptr<PrivacySecurity>(parent, controller); + case Type::Sessions: + return object_ptr<Sessions>(parent, controller); case Type::Advanced: return object_ptr<Advanced>(parent, controller); case Type::Folders: diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h index a8d8ae6f1..1f34036f3 100644 --- a/Telegram/SourceFiles/settings/settings_common.h +++ b/Telegram/SourceFiles/settings/settings_common.h @@ -36,6 +36,7 @@ enum class Type { Information, Notifications, PrivacySecurity, + Sessions, Advanced, Chat, Folders, diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 049ea5c48..bf6340dfd 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -780,7 +780,8 @@ void SetupBotsAndWebsites( void SetupSessionsList( not_null<Window::SessionController*> controller, not_null<Ui::VerticalLayout*> container, - rpl::producer<> updateTrigger) { + rpl::producer<> updateTrigger, + Fn<void(Type)> showOther) { AddSkip(container); AddSubsectionTitle(container, tr::lng_settings_sessions_title()); @@ -801,7 +802,7 @@ void SetupSessionsList( std::move(count), st::settingsButton )->addClickHandler([=] { - controller->show(Box<SessionsBox>(controller)); + showOther(Type::Sessions); }); AddSkip(container, st::settingsPrivacySecurityPadding); AddDividerText(container, tr::lng_settings_sessions_about()); @@ -929,6 +930,10 @@ PrivacySecurity::PrivacySecurity( setupContent(controller); } +rpl::producer<Type> PrivacySecurity::sectionShowOther() { + return _showOther.events(); +} + void PrivacySecurity::setupContent( not_null<Window::SessionController*> controller) { const auto content = Ui::CreateChild<Ui::VerticalLayout>(this); @@ -941,7 +946,9 @@ void PrivacySecurity::setupContent( SetupPrivacy(controller, content, trigger()); SetupArchiveAndMute(controller, content); - SetupSessionsList(controller, content, trigger()); + SetupSessionsList(controller, content, trigger(), [=](Type type) { + _showOther.fire_copy(type); + }); SetupLocalPasscode(controller, content); SetupCloudPassword(controller, content); #if !defined OS_MAC_STORE && !defined OS_WIN_STORE diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.h b/Telegram/SourceFiles/settings/settings_privacy_security.h index 52070759e..698148b4a 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.h +++ b/Telegram/SourceFiles/settings/settings_privacy_security.h @@ -39,9 +39,13 @@ public: QWidget *parent, not_null<Window::SessionController*> controller); + rpl::producer<Type> sectionShowOther() override; + private: void setupContent(not_null<Window::SessionController*> controller); + rpl::event_stream<Type> _showOther; + }; } // namespace Settings From 234ff3ba375a20dcc49c7104eef69ea994abea63 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 29 Nov 2021 18:05:49 +0400 Subject: [PATCH 134/180] Add some resources for sessions redesign. --- .../settings/devices/device_desktop_mac.lottie | 1 + .../settings/devices/device_desktop_mac.png | Bin 0 -> 587 bytes .../settings/devices/device_desktop_mac@2x.png | Bin 0 -> 1025 bytes .../settings/devices/device_desktop_mac@3x.png | Bin 0 -> 1478 bytes .../settings/devices/device_desktop_win.lottie | 1 + .../settings/devices/device_desktop_win.png | Bin 0 -> 551 bytes .../settings/devices/device_desktop_win@2x.png | Bin 0 -> 847 bytes .../settings/devices/device_desktop_win@3x.png | Bin 0 -> 1041 bytes .../icons/settings/devices/device_linux.lottie | 1 + .../icons/settings/devices/device_linux.png | Bin 0 -> 710 bytes .../icons/settings/devices/device_linux@2x.png | Bin 0 -> 1413 bytes .../icons/settings/devices/device_linux@3x.png | Bin 0 -> 2109 bytes .../settings/devices/device_phone_android.lottie | 1 + .../settings/devices/device_phone_android.png | Bin 0 -> 592 bytes .../settings/devices/device_phone_android@2x.png | Bin 0 -> 1125 bytes .../settings/devices/device_phone_android@3x.png | Bin 0 -> 1714 bytes .../settings/devices/device_phone_ios.lottie | 1 + .../icons/settings/devices/device_phone_ios.png | Bin 0 -> 636 bytes .../settings/devices/device_phone_ios@2x.png | Bin 0 -> 1263 bytes .../settings/devices/device_phone_ios@3x.png | Bin 0 -> 1843 bytes .../settings/devices/device_tablet_android.png | Bin 0 -> 592 bytes .../devices/device_tablet_android@2x.png | Bin 0 -> 1065 bytes .../devices/device_tablet_android@3x.png | Bin 0 -> 1557 bytes .../settings/devices/device_tablet_ios.lottie | 1 + .../icons/settings/devices/device_tablet_ios.png | Bin 0 -> 584 bytes .../settings/devices/device_tablet_ios@2x.png | Bin 0 -> 1052 bytes .../settings/devices/device_tablet_ios@3x.png | Bin 0 -> 1503 bytes .../settings/devices/device_web_chrome.lottie | 1 + .../icons/settings/devices/device_web_chrome.png | Bin 0 -> 920 bytes .../settings/devices/device_web_chrome@2x.png | Bin 0 -> 1887 bytes .../settings/devices/device_web_chrome@3x.png | Bin 0 -> 2805 bytes .../settings/devices/device_web_edge.lottie | 1 + .../icons/settings/devices/device_web_edge.png | Bin 0 -> 765 bytes .../settings/devices/device_web_edge@2x.png | Bin 0 -> 1503 bytes .../settings/devices/device_web_edge@3x.png | Bin 0 -> 2215 bytes .../settings/devices/device_web_firefox.lottie | 1 + .../settings/devices/device_web_firefox.png | Bin 0 -> 894 bytes .../settings/devices/device_web_firefox@2x.png | Bin 0 -> 1744 bytes .../settings/devices/device_web_firefox@3x.png | Bin 0 -> 2612 bytes .../settings/devices/device_web_safari.lottie | 1 + .../icons/settings/devices/device_web_safari.png | Bin 0 -> 655 bytes .../settings/devices/device_web_safari@2x.png | Bin 0 -> 1288 bytes .../settings/devices/device_web_safari@3x.png | Bin 0 -> 1987 bytes Telegram/Resources/qrc/telegram/telegram.qrc | 14 ++++++++++++++ .../SourceFiles/ui/controls/call_mute_button.cpp | 4 ++-- 45 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_mac.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_mac.png create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_mac@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_mac@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_win.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_win.png create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_win@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_desktop_win@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_linux.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_linux.png create mode 100644 Telegram/Resources/icons/settings/devices/device_linux@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_linux@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_android.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_android.png create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_android@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_android@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_ios.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_ios.png create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_ios@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_phone_ios@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_tablet_android.png create mode 100644 Telegram/Resources/icons/settings/devices/device_tablet_android@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_tablet_android@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_tablet_ios.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_tablet_ios.png create mode 100644 Telegram/Resources/icons/settings/devices/device_tablet_ios@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_tablet_ios@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_chrome.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_web_chrome.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_chrome@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_chrome@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_edge.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_web_edge.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_edge@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_edge@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_firefox.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_web_firefox.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_firefox@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_firefox@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_safari.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_web_safari.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_safari@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_safari@3x.png diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_mac.lottie b/Telegram/Resources/icons/settings/devices/device_desktop_mac.lottie new file mode 100644 index 000000000..8185f1dd6 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_desktop_mac.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"mac_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Rectangle 21","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,20.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[],"ip":0,"op":120,"st":-120,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle 23","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,22,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-0.276],[-0.828,0],[0,0],[0,0.828],[0.276,0]],"o":[[-0.276,0],[0,0.828],[0,0],[0.828,0],[0,-0.276],[0,0]],"v":[[-12.5,-1],[-13,-0.5],[-11.5,1],[11.5,1],[13,-0.5],[12.5,-1]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle 22","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Rectangle 22","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,22,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-0.276],[-0.828,0],[0,0],[0,0.828],[0.276,0]],"o":[[-0.276,0],[0,0.828],[0,0],[0.828,0],[0,-0.276],[0,0]],"v":[[-12.5,-17.667],[-13,-0.5],[-11.5,1],[11.5,1],[13,-0.5],[12.5,-17.667]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle 22","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Rectangle 21","tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[15,15,0],"to":[0,1.083,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":20,"s":[15,21.5,0],"to":[0,0,0],"ti":[0,1.083,0]},{"t":40,"s":[15,15,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[0,0],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,0],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-8.782,-5.908],[-9,-3.8],[-9,7],[9,7],[9,-3.8],[8.782,-5.908],[7.908,-6.782],[5.8,-7],[-5.8,-7],[-7.908,-6.782]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0.256,-0.027],[0,-0.08],[0,0],[0,0],[0,0],[0.291,0.031],[0.502,0.014],[1.493,0],[0,0],[0.57,-0.016]],"o":[[-0.291,0.031],[0,0],[0,0],[0,0],[0,-0.08],[-0.256,-0.027],[-0.57,-0.016],[0,0],[-1.493,0],[-0.502,0.014]],"v":[[-11.709,-0.422],[-12,-0.271],[-12,0.5],[12,0.5],[12,-0.271],[11.709,-0.422],[10.544,-0.484],[7.733,-0.5],[-7.733,-0.5],[-10.544,-0.484]],"c":true}]},{"t":40,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[0,0],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,0],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-8.782,-5.908],[-9,-3.8],[-9,7],[9,7],[9,-3.8],[8.782,-5.908],[7.908,-6.782],[5.8,-7],[-5.8,-7],[-7.908,-6.782]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle 21","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"apple 2","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.955,-1.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":20,"s":[40,0,100]},{"t":40,"s":[80,80,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.31,0.445],[-0.161,0.375],[-0.056,0.176],[0.222,0.21],[0.009,0.704],[-0.829,0.5],[0.927,0.074],[0.494,-0.176],[0.093,0],[0.431,0.162],[0.264,0],[0.409,-0.246],[0.242,-0.418],[0,-0.737],[-0.236,-0.686],[-0.359,-0.515],[-0.213,-0.185],[-0.334,0.014],[-0.357,0.144],[-0.344,0.006],[-0.307,-0.133],[-0.25,0],[-0.306,0.278]],"o":[[0.234,-0.334],[0.07,-0.162],[-0.28,-0.122],[-0.5,-0.463],[-0.009,-0.899],[-0.463,-0.658],[-0.34,-0.028],[-0.524,0.19],[-0.12,0],[-0.431,-0.162],[-0.477,0.005],[-0.414,0.248],[-0.32,0.533],[0,0.644],[0.201,0.594],[0.32,0.449],[0.334,0.31],[0.222,-0.009],[0.317,-0.135],[0.335,0.007],[0.352,0.148],[0.347,-0.009],[0.195,-0.171]],"v":[[3.848,4.137],[4.441,3.071],[4.631,2.566],[3.871,2.066],[3.107,0.315],[4.334,-1.789],[2.249,-2.887],[0.998,-2.665],[0.076,-2.377],[-0.753,-2.623],[-1.8,-2.868],[-3.153,-2.484],[-4.154,-1.469],[-4.631,0.435],[-4.274,2.427],[-3.431,4.1],[-2.634,5.054],[-1.633,5.499],[-0.767,5.272],[0.234,5.059],[1.207,5.272],[2.11,5.49],[3.088,5.059]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-0.482,0.565],[0,0.551],[0.005,0.074],[0.277,-0.142],[0.217,-0.246],[0,-0.528],[-0.01,-0.069]],"o":[[0.403,-0.473],[0,-0.074],[-0.31,0.023],[-0.297,0.139],[-0.408,0.463],[0,0.07],[0.63,0.051]],"v":[[1.717,-3.739],[2.319,-5.278],[2.31,-5.5],[1.42,-5.25],[0.642,-4.666],[0.002,-3.109],[0.016,-2.901]],"c":true},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"apple","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_mac.png b/Telegram/Resources/icons/settings/devices/device_desktop_mac.png new file mode 100644 index 0000000000000000000000000000000000000000..8490e5e07ba3554bca8e5fbd2c2f6806745eadf0 GIT binary patch literal 587 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&EGDPZ!6K2+p@* z8@moUh<q_gm{rglHA6dBrL|XpBPlsEG5Mjoiu}Uf)(Jt}zfFE}Dn3$Y5p>OE(p<xG zP)%@eyOTh9ZMC$8#g4mK#`(`|(|ycNrW_IC>|k+WQfmA^=>YrXjP1AImRS9)nRl{A zO{V|&$&^hodh;FM)o#AIXK8HvVaI8yH7R2H(rt~xX`5%JMuzyU3|SSTbtQ<&=-Bh3 zoh@E@+ivIXzN;tJo$|8rLD2(`_fstDcE&Vpxc~n5-+h_0EF@PN-3gw0_~D1o`*Lp2 z^IQHv&gon2e6M4UdEdK_CK*WB%&MCA{f5V+5WVRiJ+EkO|NXPZ&+OILs#d4QBQrU^ zR~UGi-F;i8@<X_#x@Pf3h2Oi@S<Cf%&gm@ZZ*N`^w|@VZy>aVr>Xd6|Omcf{(YHuL z!B6gW*y^e8J}&+<QUBbD(^*@0MXfz#u;yyk^2?c<ZytC!<B!Oseud0q&b!zDZsMKu zs$ho_^9Ld28iV%N6SeLLJpU}I_~+`S3=^;4vzESiWO1!@?yISC|El&b+jO}xP=wXd z;p)D>w=SCahK8<c<8Co|kjpY(E=J2y3>v8M9uK7JSH626Xz=elC?Y*w{an^LB{Ts5 D8HVyD literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_mac@2x.png b/Telegram/Resources/icons/settings/devices/device_desktop_mac@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6992485c1c4fa76547f914d5802ae0bb4375504f GIT binary patch literal 1025 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an=E-JloU7F{Fa= z?ack&n*v3SU%n@F(knn@jboEWk>-RphmXe}B_v)G;B_m`dn$iHtt0p^qngRp2Pzs@ z7PSaIQV_YQFwu?o?*f%yFMrE@XyrNb{6y`$vkxs^ey*{7USog!UGclMtGnY|8aM<P zl^j?)7z7oVuyY)i3j~}9>WEr9Z_XSUIk{=7QQZpq`ueR-4}bmI)#~<Q?b4-7fB*iy zpDF%h#h(59<5!$qcJfVG_1ZAuj6e0O?%ut->?_CK8D2||Sg%i)k(5l_vMS}zzbjWn zl3TL2N_8Jqn&`2+@J8_FS=Td6-iqB_p)b}Qwz{;m^y`lwJ3d-HvrW$O_{cuFZSu31 zFHb)Hcx8It{`==Oy}LK8nVT;d^(o9kW}Wr-s=cRApPp28lXZWl-SpFq^7GGc-@I9Q zx1o>2#b=31D?+rso=QtkpM3JkzkmPEo;~~Xr{%JkO4CPY4o|W(G+cQ7b)taD?6V64 zIM_sA?DSi{IYQ_0<Hu@~Giz)AK7Q;R*Dka8;GWLPbL-cxRqUQId-h=#mb0f$aT%%U zOCGDSn(MZ#{XT2YyF+KXf85N^%~d?xEM(^4VYs_zcjb&f#xr@AU1)4<Y;9%j3DV;& z|NQ>_`~Lp^8AZHo&i?-VZkp5EcK^J0FD_;6)1sem-|}isb3Jx<PteMibctoxu3ry7 zc$u&KbHeSXMTuvc(~^@Pzj(o+nKr%CfPb>grVG!Xr(4M0ynWmGm1L^Y8GSvyzTRF% z?zivW&6_#Xu?r-2c>dpK&(t<ua8NKXF=-L`zlq2A;Z=1ri;WZ3l$MlydHB%rMvHo6 ze+xstoY?1=FJB%jXl`!iI{N*lWsQxkt*aHg<nJ8)cPj2ngJk98+!kLvecF5W*=Ge7 z3Hj{ScXG_C3knz>`x;2JX!n*_{k7fq`NuS+sc+_rZ+uXi!+f&l__ns<#5HsEr+UrN z&XN1~`Sa&juU7rE-Q~sdM0vs0EPp@03Fj3*ZrZ(j_s^e|Ki~Z8c~W9^G&)N=S!v>r z$W2F<n~Ck&ym|Bb_4=I^`>U+ypD#YczkGG>R;MKreg}KplB1vP|1LUX{lRA<{}#U} z+x`33FM}#WDGP;tJ9gZ-cW>X`y`qyRZX%Sr83Z3VKaj7OvbM8==|TZ8_c3_7`njxg HN@xNAE9%jK literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_mac@3x.png b/Telegram/Resources/icons/settings/devices/device_desktop_mac@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75e0e14f8d4d8026411891458e6e156afb3a02b2 GIT binary patch literal 1478 zcmbtU`BPI@6n<eTkRWRfLjZ*oEr?Bu1Y$sfWC0Qu9mIfC41$0m1Sm_8SV%>yA`uN! z49GqLu0<&zB9$nNMF|nvWesBl8q+|7K@oVNk6+q9z&rEZ?>qONId{&Sb5H)k5dST3 z6dVA6EwlhiD8#v&Fw}u|j_)2BM6mQwe-hB}%2ojxl(-05d~h(Z3(|%F>;enW+jN0| zf&c)z)i6L8V%X+cwa$OHxz)P=>rF@dYgT>$p#MFM;v04frkV_N@?hE;{4)w(TRVTX zI53$;5t8W1b%qFJgmaG{=7<x*0l|2&BQDn0A>Ywqhe#j!J6eQpyy~^Falev3_#u9J zskfBSm!e%9O%!|v=hlgR+4BUq`QA09Rjxe%Gu#j8nEedseVL56GBh%>wzejCd9AIi ztXz=w&Ckyd4-XFwg@uLvI(D1S=2NN9LD1VLwHWE`?d^`kt&i|NXp73r*R|S)&>iw9 zDwR5?zj1%Iu&~hI-(MgcF5~9ps8lLWbH|OEZvz4Y3oc)F*L>~gXbOk-^!4G<O_?DH zN@j+vFh9TF`(|S2#Kc5f8-dA`4Ge@1>n2S(*UbL;k!Q4##CkC_Fkpc~MTUpBK7O3& zqOK|wy-tfg4OYPi**=#IB@zi9kN?i7tE+3dXSc(Zd_`4N)rX}e6I0Wz^_`uhxU!A& zjaIf~<)TW}!o@l}=NA;XX#}e~@h;v|d=q11C*-qd&ql<%)(9yj#oC#fnT3T{Xd5K* zVRJJ@)?8jAZk68Ch(w|=K~qyYhohRFK1L#Sei|=XbnPDf8+uluaGtV(vOb-iEo3Do zzF%5Oqth={Rmqe}<;Ra6TU4wv%qJKO*4rP4yMOyO4P=1br$<Ld_PDtjO${7<KQ}fu zMkbSolnO3CCMM6%ePLmN?rLIULLd-aTwHvJXIIaFAV?26lzj2E_{o$0Cq;S5Y)~qd zzQ5%&;c}*Ov84lA?d@wyrG$|oQ9~s{AQ0m*vlQ1lCX>0{&Tcqp=G{9=Z17xGM392j zk%uw9uJH2oT<JTNS}T6^=s`zt%d+l8fgmX*rPZCTRof~QiQnk>nhu?`cP#P^+3mjy zf;qysEPANp(a7EYJgF|d;QHOWcX33a<&U*i&0Yk8xw#N25l(k?k!!WjpHqdYtVSrx zOF;mrNlJ)~B|Dxik0{hYU53G6(97Y65BJmYLSu^<!oXk?Es}~3-PblqZvFf@Tj=Q5 zomPAEI+VJ;i;s^F7K5qRkI2gwAQI>YEbVch93!|426BEZvR;GhNMf@sYg0I!t-aAF z_a5TiX{)c_iAIys)wrM@sdU&D<8T%0ow;6GYAwmA{>==xAGK}ULwEX;`X_}^Uc-=F zE=N&TS63}8E#38cjE@H&FpG<eyJH1DeY&5wH%R$|I5v~Z_3-e>m2G6H%uCB!D#>;> zHaF_TL?V$!qeVqUb$567Ftf6<Sa3X%SV<IUG#R{0)$6nq<j%=^jZwZ>o;iinXz^DY zGgY8Maiu1_cJ<k98m{GoiTr-0ZwemPUh(G5F>y-^wcwnJQ9fch=p}FNWw3E?H^24z zc6~S;ezLgs;acV(b!u`lEIeGf<m!1bq2|az^~-(g_yEcNMbrK=G8T)?OBgzSP7XEX z8^iQp25k#55>{rlY;4R`R1wWq2$$vZ8_^c<(KESeX`z4E?d*Aa>llxf!sCr+FiXZ! zLF^4Eeuly+<nf8pii)~Xh9ZZ<=ks}2Xrl6aMX&iF2~lETV4#@wRAVC;bv3SEK6}ho by!5pC;`4o9xk}g6n=_O~4WTrUqVxU*y;_12 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_win.lottie b/Telegram/Resources/icons/settings/devices/device_desktop_win.lottie new file mode 100644 index 000000000..979b7d8ea --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_desktop_win.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"windows_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Union","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[15]},{"i":{"x":[0.833],"y":[0.889]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[12.5]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.222]},"t":35,"s":[15]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":55,"s":[17.5]},{"t":70,"s":[15]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[15]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[15]},{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":35,"s":[15]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":55,"s":[15]},{"t":70,"s":[15]}],"ix":4}},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-1,11],[-1,1],[-12.667,1],[-12.667,-1],[-1,-1],[-1,-11],[1,-11],[1,-1],[12.667,-1],[12.667,1],[1,1],[1,11]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Union","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle 23","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[9,-9],[9,9],[-9,9],[-9,-9]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":25,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[9,-10.667],[9,10.667],[-10.667,5.667],[-10.667,-5.667]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":35,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[9,-9],[9,9],[-9,9],[-9,-9]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":55,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[10.667,-5.667],[10.667,5.667],[-9,10.667],[-9,-10.667]],"c":true}]},{"t":70,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[9,-9],[9,9],[-9,9],[-9,-9]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle 23","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_win.png b/Telegram/Resources/icons/settings/devices/device_desktop_win.png new file mode 100644 index 0000000000000000000000000000000000000000..713026f682009d2de2e28142ec09c1c79b15beb8 GIT binary patch literal 551 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&EF`PZ!6K2+p@* zySthlM9#iiB#@+~s(D0hZfmYWYwCfZ`U7e=xPA&(zYh7?_{Z7mqSA&XKEek4E&_$$ zg~TUX^v#Yup_j6ZL;d-@o!2ehkH+=uy<#YBuyT;C5M{q-#cws&PxiPV%fjGD@g<qN zqSl(t_MKZJ;XSEjZ=8K+*u3YfRy}ez`mt$QX7fRY^_i<T>2ROh8tVN|Hoi^F_h8xX zyYIgr@K>8$w_l!x@xjH6C9i+YmDss=-Ajr1>0vhvdLDn|sDJ)>=OT@L@0rsi*pix7 zuS~ILlzpZgof3KNN%x|L4YO_LDN6Rg{}C|t$e*uOwN`Tzd}i9ZFwA)V^I^e{3poOl zd$#BPF0eR|cG*O+`ut~xVj1z{AB{KpPg-K5+Q4;~_oLKJ{pqI-cn&t)%u(xl^s|P| zMy{W8lYVT@-`3k*FIs=^&D<EVW!pZ{e?JbsmZ;wu^I>}b`e{2KnEqd%6?rXRDgRw* ztazo@mY=)t_B~K^<I^~)(KX@fwe_-*)2!{qCdkfyay-|x@>Br(3g#<}uMQN;{$NU6 Wtu-mUeTNSyl004gT-G@yGywphjN1nQ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_win@2x.png b/Telegram/Resources/icons/settings/devices/device_desktop_win@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1c9da124375aa58cf475bb510a85508b574e6068 GIT binary patch literal 847 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an^x>foZ3wi(^Oy z<J(!b{fv$h$IoZ)$PnRb>he?JlIm@95)r!i%4Ow*j9P|^KY4`Y9h8MGW@@-7f1Jdj zDA=SRbjWwzguQort<%c;p4eY~v)TCk>Gw0|rl)U~m(NgOV8V~qJ2@y$Rpx6y{In?3 zL~5l-*Q=dz>;Km6zx{UJ`Sgp7Yg)^8?~Pk8ynV*jg9#75|BgJaxy3!`Zr=8pt+mEm zZsz2e&AuSNY*L0vRdGk5`uy|3ZL5~ddH=CO!_>xh?md&-4{Xg3Gv8~fUz)v)cb-wh z|GDeZlJ{NB674?vx31pUz3fzsp1axF5-Xq``9({EB>RsiZ@l6AY1dq*g&(HZo_qb3 zD_Nm<_St0t>n^|CcKhx6EZ65;TklRZi2C;V=bt{|J<-P>Puwk5QLeftMPyHloc9W@ zgWepU*M=CL+T)~nlhey-Pmfou+3Z{sshItqE4y8kmRwPt?0N57+Qx`AVcO*%y1)I8 zcvZJH+MlOlc91w<`^i(!&DMrRuMAmsdF927C#O8;r~E6hFgRhhIDKAC-2>KQtGQKs z@1+<$Tw7@b^t<kK?|T)NdXlXA?XSOB3EJ2m{(hz5%<1{&^UtfBeENB}r6}mh*|g08 zdOKS#di$*mS#>q*&Sx9V(uDyY>#v8e4t>|QK4>LVV?)&0Lxy5z9>+XZEV<IXOizNR z>y#wN$1}zae<rC-_&ME&n|XPLvAtBUM1^Knl7&pyDMc2>OwJS|$s&>3M~9i4HK(6$ z-Q(pc{hmMfNyIq?9w}k9$uHZFm+Q;j{$MrtTx{<DnqnI{eKGExa|(9f-F>%D&3Mx} zUlk#K*MA?xQ<q<!>9zF4dJX=W6RSUDZPl87`pJ8VoxG0r91RTk&<W26+<zo$bNz1} QWCJB;Pgg&ebxsLQ0LyVzMF0Q* literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_desktop_win@3x.png b/Telegram/Resources/icons/settings/devices/device_desktop_win@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3d42cf390a8ad1c58ee15f9d7428754b609b138c GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^bs)^a1SE4EjqU>}#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1ATfmH9gA^)E6L$kDul96t45?sz zduOkomZQjl!uPHQZoCJ(GT!t~WIw=^EhKl){UPg?%ZCC2K5&$Y*)WKTnu#PSE_8mo z;9|A^9qpZJ#;?<E-n8Vd=dw?0w@k0I<~hT#d*eY~Miv1F21<xVQGpB-sk?dKpFVxs zf9iIQnKTdE*|g&9>}=L8|4S3At9M_1xq`)9<?DwJ0V_koT>k(1b?bubQP;&6Gs?@& zKmSTkPnVaM@0xpd<=s1XQs(Wun>X8f`R*@o-{uw<UtakyfB9PP$Li|p>zsFF?2piq zlat$(QF(D$@vpsm_quM#`TNkS`1zNckNk|IUtGTOPmKFlzD(x7#na=iP205PXX?(A z+1I{*FQ2$;*Dfs)u47TZfB)XTeS2ost1i{1k8y6-UVr^C@5H9yD8Bg0XGJ?-mPqM0 zUcP+!1Isj<RpnRg?d{vY`#LOj`fxccSWa^C$s=V~@^{8n?bp+EX}4odDdu=_R!?(^ z(|o2)mMmY=ri*wj^k=*&<D|cL&7?D$^;6kImBN1Sn!M<K)k}Rny-z>)^sb6pD<?01 z`eMh`oVRO=D&Jhq*`u1#^gZhS)i+xG$ItJ3Ik&*++tHM|{!O*t>bFj{pV-ECY+k{k z??+XFro7$kx>Z8>;x9johrg4Oq(V9(&KhXchy%6MoVMua-&>p%&F~`E=-lar<$gKb z410Z#Ra$;I*O|-s<&wOt?9EAhrxxeTKY9BRJ3ITsw?C{_P5<}llTk{baO&?hAzCNd z+ny>b?kli8-n(~J@5X;V-}DsK?F57OF1DNaE;Em%DnFgQ>cw8p3hpy2+V9P7*nXe~ z!hKV7<^KKq|9`km+J5ggGk^Qx<Hy~97xpPMFfe7SDa_u!$<bo|`N?<pUb;D}#(Bl{ z*MD~`oO?;Y!ruP+$tQZN!&Yy#$zxhy@TAE6^z(^p#mdUcrukbi{;JzAU(;IpGiTGK zrZbjNL3?-aF8==F+O<tFtGq8-$A8*lEZ4tPa_0$mz11r7UtBkO(rk0;=_Z?>Hnz5@ yA7}Xo`dxbcdUcH1`moh6i`1nlOW++UAH*|ycl^~_`MwI29X(zBT-G@yGywqCl(1U> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_linux.lottie b/Telegram/Resources/icons/settings/devices/device_linux.lottie new file mode 100644 index 000000000..bdbaff768 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_linux.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"linux_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Vector 25","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[15,21.686,0],"to":[0,0.069,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[15,22.103,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[15,21.686,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":30,"s":[15,22.103,0],"to":[0,0,0],"ti":[0,0.069,0]},{"t":40,"s":[15,21.686,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-0.452,0.127],[-1.798,0],[-1.471,-0.414],[0.428,-0.195],[1.333,-1.348],[0.372,0.376],[1.412,0.643]],"o":[[1.471,-0.414],[1.798,0],[0.452,0.127],[-1.412,0.643],[-0.372,0.376],[-1.333,-1.348],[-0.428,-0.195]],"v":[[-5.66,-1.69],[0,-2.686],[5.66,-1.69],[5.712,-0.738],[0.726,2.404],[-0.726,2.404],[-5.712,-0.738]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[-0.452,0.127],[-1.798,0],[-1.471,-0.414],[0.428,-0.195],[1.333,-1.348],[0.372,0.376],[1.412,0.643]],"o":[[1.471,-0.414],[1.798,0],[0.452,0.127],[-1.412,0.643],[-0.372,0.376],[-1.333,-1.348],[-0.428,-0.195]],"v":[[-5.66,-1.69],[0,-2.686],[5.66,-1.69],[5.712,-0.738],[0.726,2.821],[-0.726,2.821],[-5.712,-0.738]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[-0.452,0.127],[-1.798,0],[-1.471,-0.414],[0.428,-0.195],[1.333,-1.348],[0.372,0.376],[1.412,0.643]],"o":[[1.471,-0.414],[1.798,0],[0.452,0.127],[-1.412,0.643],[-0.372,0.376],[-1.333,-1.348],[-0.428,-0.195]],"v":[[-5.66,-1.69],[0,-2.686],[5.66,-1.69],[5.712,-0.738],[0.726,2.404],[-0.726,2.404],[-5.712,-0.738]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":30,"s":[{"i":[[-0.452,0.127],[-1.798,0],[-1.471,-0.414],[0.428,-0.195],[1.333,-1.348],[0.372,0.376],[1.412,0.643]],"o":[[1.471,-0.414],[1.798,0],[0.452,0.127],[-1.412,0.643],[-0.372,0.376],[-1.333,-1.348],[-0.428,-0.195]],"v":[[-5.66,-1.69],[0,-2.686],[5.66,-1.69],[5.712,-0.738],[0.726,2.821],[-0.726,2.821],[-5.712,-0.738]],"c":true}]},{"t":40,"s":[{"i":[[-0.452,0.127],[-1.798,0],[-1.471,-0.414],[0.428,-0.195],[1.333,-1.348],[0.372,0.376],[1.412,0.643]],"o":[[1.471,-0.414],[1.798,0],[0.452,0.127],[-1.412,0.643],[-0.372,0.376],[-1.333,-1.348],[-0.428,-0.195]],"v":[[-5.66,-1.69],[0,-2.686],[5.66,-1.69],[5.712,-0.738],[0.726,2.404],[-0.726,2.404],[-5.712,-0.738]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Vector 25","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse 28","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[19,16.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[20,5,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[20,5,100]},{"t":40,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[2,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 28","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse 27","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[11,16.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[20,5,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[20,5,100]},{"t":40,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[2,2],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 27","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Subtract","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,1.422],[-5.523,0],[0,-5.523],[0.535,-1.225],[0,0],[2.761,0],[0.81,-1.668],[1.979,0],[0,-2.761],[0,0]],"o":[[0,-5.523],[5.523,0],[0,1.422],[0,0],[0,-2.761],[-1.979,0],[-0.81,-1.668],[-2.761,0],[0,0],[-0.535,-1.225]],"v":[[-10,3],[0,-7],[10,3],[9.168,7],[9.5,5],[4.5,0],[0,2.818],[-4.5,0],[-9.5,5],[-9.168,7]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,1.422],[-5.523,0],[0,-5.523],[0.535,-1.225],[0,0],[2.761,0],[0.81,-1.668],[1.979,0],[0,-2.761],[0,0]],"o":[[0,-5.523],[5.523,0],[0,1.422],[0,0],[0,-2.761],[-1.979,0],[-0.81,-1.668],[-2.761,0],[0,0],[-0.535,-1.225]],"v":[[-10,3],[0,-7],[10,3],[9.168,7],[9.5,5],[4.5,1.25],[0,4.068],[-4.5,1.25],[-9.5,5],[-9.168,7]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,1.422],[-5.523,0],[0,-5.523],[0.535,-1.225],[0,0],[2.761,0],[0.81,-1.668],[1.979,0],[0,-2.761],[0,0]],"o":[[0,-5.523],[5.523,0],[0,1.422],[0,0],[0,-2.761],[-1.979,0],[-0.81,-1.668],[-2.761,0],[0,0],[-0.535,-1.225]],"v":[[-10,3],[0,-7],[10,3],[9.168,7],[9.5,5],[4.5,0],[0,2.818],[-4.5,0],[-9.5,5],[-9.168,7]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":30,"s":[{"i":[[0,1.422],[-5.523,0],[0,-5.523],[0.535,-1.225],[0,0],[2.761,0],[0.81,-1.668],[1.979,0],[0,-2.761],[0,0]],"o":[[0,-5.523],[5.523,0],[0,1.422],[0,0],[0,-2.761],[-1.979,0],[-0.81,-1.668],[-2.761,0],[0,0],[-0.535,-1.225]],"v":[[-10,3],[0,-7],[10,3],[9.168,7],[9.5,5],[4.5,1.25],[0,4.068],[-4.5,1.25],[-9.5,5],[-9.168,7]],"c":true}]},{"t":40,"s":[{"i":[[0,1.422],[-5.523,0],[0,-5.523],[0.535,-1.225],[0,0],[2.761,0],[0.81,-1.668],[1.979,0],[0,-2.761],[0,0]],"o":[[0,-5.523],[5.523,0],[0,1.422],[0,0],[0,-2.761],[-1.979,0],[-0.81,-1.668],[-2.761,0],[0,0],[-0.535,-1.225]],"v":[[-10,3],[0,-7],[10,3],[9.168,7],[9.5,5],[4.5,0],[0,2.818],[-4.5,0],[-9.5,5],[-9.168,7]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Subtract","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_linux.png b/Telegram/Resources/icons/settings/devices/device_linux.png new file mode 100644 index 0000000000000000000000000000000000000000..9bd1ab9c3a586b78af2eaa75393c612189cdba9b GIT binary patch literal 710 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{Hv<Ecfv1aONCfBG zu)V7r110vam-Y%wNi^KBF;eFrQ=nP*)4YF-s}DY~4-lI<<>I;?o^ZAxms8svgC;q? z`}=h7-MHr#@-F2&-b<Fud;b07oaYxS?|*;m8~=;t3+JEhoErQlIR+9<2Nzs_UAp`3 z1bwmYiy2q4ww`<bnK`fA!$!{EZhrjQu=p!cdea*mb{zJbmt`VVyz|b-iY2e&R2>+2 zQ!3L9FF!1}^7`v)t2+!S9!K^W@pK=(l(&87)4YZW4jPa3AB1R4mHsBA$-#Qf$wI2v zOC%{sL3~<*P{Bc~xqO$SnHpr&&M?&|$nbej5eP^)wDOR3l#W>N)Yo6Frg}Z}o9&}! zlO@#YviRbKSxPQK0vrn(c{;s*m|py}^xN4q;kBWwLziFn<S@A8qqf+rkJmQQLxF`+ zttHHCj^FaVap!Z)HnYf2KkB5|BBQ3$q|omkp&=qPVTZvJgNH#k64r((JCsE0h^-CN zp6H&j`6ff7gToe1$Aq?*C9@7Z`)v8|oQI0j6G0_T4i=_GzFS(#a~<BWHy?aha6zs8 z@WToly{TS6%V!_2Gn=j16{I2Z>+e1>pt=|G@8y5qIq>(}@4t5Q{cG3enn~B>mF$k) z^C|v@uhC4M>8B^3blLmvi@t(@L-jw!3opucFZI48dErgjZ*RW|XVbp_wv7!s%fR$7 zQ(ns4`ha{t=Vf(=OHYgLPO-6<>_1+<du|m+j)2{Mw)=(x95r!bW;M(DUI|}NU*PXB e|G;^ceT-Y?AM<!RY4bu*8uE1Yb6Mw<&;$Un5F4}r literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_linux@2x.png b/Telegram/Resources/icons/settings/devices/device_linux@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e6eecd5770b3c58538b4178de6a68ba363fb2bce GIT binary patch literal 1413 zcmZuxc~H^`6#gNkV4j<IC?1(tibjd%0jVkK;)-}-Vdj8Gn533g7I~)3+IS&(%WhV- zn#UkgnV2>y-s^g_$(ZG_rm3+Ssfo4t&+as{e|+zI*Svq;dmq!^&r4ZRPZ0nBWpA8E zfQ$q80F{&3VptAaMxdksFE^lZ7``M6JgA}GQFuIHDf6KKC?^(x?6t_CCj$V0IUoQm zW6)lXBlmALkOThD-)rQ@3}FEP^3mJlWZ)IhYWcL)@zd~Lgz5qz+)YR$^ow8%%V!>A z*VlY(ge9}#zq0(}rLcHm6{~2!1=48I&*qt@QImpEllryc#TD{cYjn+psG;&`Ogs0l zNPg6#na!P@uL5qgEVum`f1w|=kd}yb<iPz^%oSMR??O3YI_K<tNpC)aN~M~bnvzJQ zva+(#(NT4Eb;`}_*Ppewi)LnKrl-lFp`{fSsW)!yQh%$&Yv}5}9v?quXLqs?gOAHn zSAuZ3{cx(PvhwQ6%6wh9`D<S&_HBWKWq|8de}Dg^q>a(h!aL<hl26KMW~~RE=+e>F z=C`&|T6&P(Mp=_E-ciSm^>r0h)lTzMF*;#F>iLp^AToJZEH(@*w@{m}EvP4{oW|pa z78f5pG>(H5W_ToO3`F(~4+}aw`2^NJb0*Hr_X*kHzSWO}BSOBu&xxX`3G00rCns7v zd;oH&#Q2Stx4U~L^<5EA+h#g_*x}G23Z4E_fHgZ7cN?fM&3z#fiL8CuiZL|Wfe^Za zu9j9ZgJHwd8lXV2w=QbVWETbn1u>b-?u!;!NMLZVB9W<mHYCKXGRU}DAP6tTi(@Su z9JbcikJ{T$n%Ir}GB!3wBobv)rDh~H4v)wC`dZo8#K*?=35DWZ^qK)2o_G7UH4=$H zAZjWrn;}Ik7OS|}y^zP_^@w(chVX7~awv>R3DHGkE+NC!#YG~KWas4U;7DO%Pr2MV z(0H=0@pA=uVn6{d^YOvlztf8_#~d8$LYiAz3ahK*4=Z~m#>C+AFY}iuLM1TM9*veL z<=TN+S|~@yhxel24Rz>6S9jdK>z+9l@yeNv`@`DC=DbpF3xt2L;VRO3Xl-dJq5gpp z$MYLDT?Zte01a`fO)Bdf8PSf~e!+vi@Lpx)dgj?Oq2h&w>{%~QPmky8wot5m<UFA~ z;>*^SVSV4vl`z*XHHhKd?k-skDL=;$w6%RUWs@ROQc?(n(L03(AS@ua`5wJy47TfG zD5RFAf7;yK@%vpQ8aQ`uZji#5bqdA!H1AO4_cIy7KMizruz4*rUDYKen(VO~W{(Ae z<cY5zM+OJ;K1S@Ccj<|jmwhmlIFr3;D3^?+)Hn5ZsgkO+hoxF#skDLD8TUR@a-j)J zG3@zJK&+CYhyelDxoCTPK}Sb|QW-;ALt{&2GXDo9h~Y)tC-)xv5f%2Zg-IsS#oP*s z%x5xN$^qGF2d7(xJBllu#M8WOLhakgTR|`T`uei7v!BJDBV`~_R~Gt7`kI>iROu1% zbovuy;%F2s>%kY<2Dw~Ry#-`e|A;FZedi?;L#~+c)uuVzeA2ePz5VEsb$YiH{O*eE z70#YH6LUbbLb=ZH(l@-6ShU^HF!8!!(h457p(oYSMIc5HlQd9wm73cJeo@2W#=5#t zGFh`R80;ojGO^Nm%K0ftG;Y~44(QZTX>YjQGgDLNm%ez}s-@|9N1#LLaDDybN%Jt2 zpbWY)JH1RQb98apMef%*%Dr^pxu-3E;~J8K0tBsGx-&hhr?*$qTSG2xqOC-4V!$=U x$(Jwhu1>{324CI#n&M~iKhpA@(ny=&TMACyX+5pBiO2RN(EF61N26Or#y|G0Z14a8 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_linux@3x.png b/Telegram/Resources/icons/settings/devices/device_linux@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..728a922cfa9fcc6f5dfb2d680449a6acfd4fa7d7 GIT binary patch literal 2109 zcmb7FX*d*W8=i^IjFBb6Fvv10TQVvdOH7O<OAW_*ED_2WQ<P*FOro-fp@_`bvh|I= zj^&WDO(x7_Y>~)L_I2!M{5jvB@6UO!>wfR&e&6TEdp+;7JiTUZDgcp!00001b2DQM zmx+e}AK~seBg8P5fI%2j6riG0dWI{Ey&cVctgHYkTpJ7k#@+_-9=dQL#RUMs^BM@? z;WF^>?Ddg<Zi%mX{%0RL%8@0|005}S+}Ox21UR4VVkYJSrL!$9bzJiiA{72JerK+O zO?YMUD5Tkz)8XyH0-+E{s478q_PS<SESx0Fn>UzeED}16mbJLXKI?4c!1!Fbsw-&O zH23Y@W_#KDSCS16``g_J<p-MwM6Gv2*beQ^7m8WJ!bebsU@%Z#jyF2;UkJHM-b#h> zu>IYwmX?-MhqrFkcYeCFVUrD(_*{c<DOp)rC^R!O^N9sI51pHy{yt!+wxOts!C<7T zI&_VqHZ~WBsZ?r`tp2udC}CxsPyOuPgd*q&?4rL8VPlTN(U}XwA2a1ujs<kxR7}pw zA}oy<jrp%z>D`qheO3tIKaD>)ot%;JV49UDq2UH6+4hih<iYUf1L6LfpsA6O*DB2m zNM42Sx&7~g@mnhs4i$ggjV&0nC5eRgdgeB1WJE@oJ(3nfd3p4Hozfm%9-~L>ZHq;_ zF43SLV|Ijk9c8kzv$NwQG=2jWS^R8#%<uS2%#(gIGbQ-`{Wp|C3(!?)(0S=pG|_3e z?xC%i#(N|*ZeA$j14b8#>|Vi?#?*%p7rBveR#%qs%ZzW+j(>r+vPuxdAkWkM1`>BW zUtYjEcK1Y?$DXp+lzuo3KlfnaJ7IHS#v|WGw5dN)FWkOtO|z@$dw2H<+}ani`VMEA zYGAmXMoZWNv+-Cg+tjoQF-d(!>?!#@H8r&sIK!svDNj>#%Wbwlrzobl!9H>2*jRAJ zCA03v#>OdTuIV0`#f|%=W$u$jc}*&5wVm4o0|UiDAW7w9GeodFE`qa^_W}RYQYF>Q z?6~d#C8QPf>dhPFlT<x{WeL(fY4)-H;tBCj`+GZVnn}H*udlC-4fF8#8;H2D0*n(n z+{whGWn+GDb72TJ?h=RhTf={^Ugj)&dD2jks{7z_M;fKL_|~u9@|}%&MSF|V{41Bt z83B3IhVvO;+BjqMq@bxzpmu?SU{e$iD3m~O?Uugm3FeW^LGWHY_ejZ9hMIB@GaF6e znNoh7)P|@D+gcfTkFA%QsDr^^R|{|Bid<gyW%uxq&-wJw7E-N~<Cs!riv|*vJykW# z^$fMFUx1cLeTwSZ?REcT;YrI`Km74@u|C;<V1QvpG~w{8{J)#+99&2Cy+4GNw- z+DC`m;}~^HZ$?H3#5%JtdA-}`R+Q3Jaxo-~vvecN0Ds%AAnEsGgQ(;*mGA`Q(9n?m zuo{q@mR6kbV6I;i3;t2kL;nx%Q+j@P42$AZE?U@ntFOWp+1TCJ7ZgxQqh2{V*b6DY zwOcJMDXD4GMkb5P{DGi8eW-Q(Im41=c>PC11YT!<V;+{8Vd$)dgs$d$jH57Gvr6?N zlWXJMZ)|50z<3SJIaaD&i0jMF_^lJAN}9!#Q%AkNotCnp*N5+rwF=(8W$Hm^7S-J9 zLb%DqXebmm7GGX_m8owYeb0>H&$pPk9QOGu)l}apf3%{eW{aCHtw%fmOZ-GSnsY*0 zSJAUVSk4;!wjdl%QhFZPE#xrv2qd&rOWX^!`BMao9vB?dSJnweH|vlmRimT{r?Bwj zt<UW$1?Bx3Lf2K?B-EY9+fwge^zaykcQBcoaL-@ucUBRIuY|qF)(Y`T%2^>xRkL_u zgYfON$ALbNa@v=5D$6ggyO)&=O6k{ohj<=$@|1gEB_c2LJaBPx%yxbH#f`w>rqcB0 zojZDwObfDu3^pQ0(Gz#$vSxDml~EUQin$6+|DL+Hw|5nOc*8HwU9R`3ZeMkuhBy7m zpO@vM<&wQ@GSFSh>gxArgeggr9a&RN0@oJ4*EAg^9_;Vc)*??r60anqgq@nh@0haP zk%>O}*Z=91pTR)ai3&ysw*+h}no-aTAhqaa2Kt13*EfSO&PUZ%K8sh5SUXwQSdWme zS`y_YB^2Yxr$=C$SJ}!jHzwQGriN$A8ya$pC}(hN;Mr=QCoEfLr*luCRn|rPkM`0& z$q0n_uJ(Cx;^L3`FRG;;WwY>KFMe*kFl9>Cd(jhTXZ;xQ9BrcWs;_I52-Br1!1b@- z1pX`7vTJdxAtkg%|Ffz-fw3&jhX%cLBWpD>10L$x;eR7_JN?UA(x)#O8tla~>Qp?8 zm15`oa@TE~e9-sST`R0oH1TzGtD!S`Y~c1xSHWqUoZa26ARj?Mly$a_5vzJ-En8w$ zQ82Ps6>t{?71d<!dtQ!go84NS!fjfT+y%f2rFE_FnAk_L{uS;Gvf0&nk#<NSp_XLo zV%o;sH_@eoIfV4_;op4L4I!D%E-rho_K(?Nj4zZ50c`F%^V4l*`H)&S?G<e^^{~3T zimWu&jG^`rzY%2hS-hQ>7uCrqCj`oQBA8-+DiB?T8=TbHqNF4#WLP&JJM1(=e?91Z zms52ft(Nd)VB)vlf~y%EjcNZmUbT++GrTnQe>dd*?_e}k<J$$TfeNW@u#pu2cbS`5 K8&{w_NPh$E1k#@X literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_phone_android.lottie b/Telegram/Resources/icons/settings/devices/device_phone_android.lottie new file mode 100644 index 000000000..355d94431 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_phone_android.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"android_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Eye L","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[10.5,16,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[20,5,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[20,5,100]},{"t":40,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3,3],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Eye L","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Eye R","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[19.5,16,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[20,5,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[20,5,100]},{"t":40,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[3,3],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Eye R","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Face","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,15.5,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.335,1.25],[-4.973,0],[-1.361,-5.079],[0.382,-0.702],[0.129,-0.123],[1.573,0],[0,0],[0.579,0.551],[0.085,0.157]],"o":[[1.361,-5.079],[4.973,0],[0.335,1.25],[-0.085,0.157],[-0.579,0.551],[0,0],[-1.573,0],[-0.129,-0.123],[-0.382,-0.702]],"v":[[-10.536,2.379],[0,-6],[10.536,2.379],[10.657,4.957],[10.279,5.449],[7.34,6],[-7.34,6],[-10.279,5.449],[-10.657,4.957]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Face","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ri","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[20.75,9.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.25,-2.25],[-1.25,2.25]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2.5,-1],[-1.25,2.25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.25,-2.25],[-1.25,2.25]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":30,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[2.5,-1],[-1.25,2.25]],"c":false}]},{"t":40,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.25,-2.25],[-1.25,2.25]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ri","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Le","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[9.25,9.75,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1.25,-2.25],[1.25,2.25]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":10,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.5,-1],[1.25,2.25]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1.25,-2.25],[1.25,2.25]],"c":false}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":30,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-2.5,-1],[1.25,2.25]],"c":false}]},{"t":40,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-1.25,-2.25],[1.25,2.25]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Le","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_phone_android.png b/Telegram/Resources/icons/settings/devices/device_phone_android.png new file mode 100644 index 0000000000000000000000000000000000000000..19b73ecd009fc0d9796501b906fbd4f9fd397642 GIT binary patch literal 592 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&EFwPZ!6K2+p@t zHg+9y5OLj{ofR!8t(di{fkUaxam$7e(m6*?8P!kV|H%G^rCQsX_2Qw1mrb{By<E4W zy!?USu_u{LSB-*O%)iewKflu;YgUvN<J1N(2h{~a9I6*`%=(WfFTc#0xiNM3T{{`R zUN_}w9$697N_XEio9(MM`C&C<&i3d784pjUysNO;cmMt2hZFLd8F&o(C;oO6jThl! zJ^%UK^WvpJhQ6Dxzm{xc6pVEgc=P@D%aUEU-yTTW9BCl%lUaE8y4R1=ez5*<(@SLZ zuK9Agt;TMDgpSg3FC~Km{`@`+S6-L4X!a&<Yq&TmfuZ@4*7D1pl46+)pYlpRP(1s* z*pT7;$-+5#+jmE;m2-D$VP%+6aPh~Ng+|x^ZT<Dv&Ue<?w5GhRQ7S=<fjbU(xa<~q zyl;MC+XeT<f{aI#0<V|moPKK588XF7^$J77M2S50k5?Ckt-fgMvN3MG`s9;etEQIZ zC>~-`h_rZewNl7jf@jI*pq#>vlAMaut^NPE8q8YvjQ`=W{STjJMCncU{=VDv_O{yl z$DI%T;?rH|5&vobKe0QfpKglLTYWX_)W=o6z;F%VT)`5;^k2PEUUjv&;paKW3PG{y M>FVdQ&MBb@04Fu>;Q#;t literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_phone_android@2x.png b/Telegram/Resources/icons/settings/devices/device_phone_android@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..331136c7874362dce5815abd670a64a3de3b2237 GIT binary patch literal 1125 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an=E-{Jf`&V@L(# z+sKVx+KwWji!%gR-?nJD2#9J6>M}L4Z1k=>sMR&Mb#AX%(rU3=kr6M&XM||_DBs}j za$@4tXlhc*SoB`@-T{kuIrGxW)IQpt6MJ~>Zt?lO-+hdB8)s%ca%Ek?Ai97lq=74d zQOf~4=TARZPjBzjqMfT(ua3}3+rIzg$&;r~pPoE<^23LTyU$#^VKnp1di(1?e^zE^ zuhxwVXbdormzTf2h}FN_bzO~}{tY)bx1g0%bkl$`8$`KSqqj_}RGN6=^XJVo7R{bL zd)>Nq4>%o|v<`9>E#Wx#vMVt;*;%B^|Hi?~5$Zf~L7{BBg`YlJy<}QmZlutX{{H^# z?CjmUcT2P#PCRq_&!0WB@-s3rN=iiL+uGVbDsP<d`**d@vxuu7RyGOp2|Z-go$%`J z{EXGzpMU@U{o_Z4hRCd$GjHC!d9X;eUSuB|Ljz-v0E<%0fj4$`cUDdFP)W(Ze*JoR zd3n(fUuH#)DG36**51&oHk&<lDZ7b7>UOj3IWtz=zJ0sF_H;qOUuom}*RHi)^}CjP zkwN#wUB4)SZ|BaPE3wMmn8+`BVxp2q>$)VMQ~mw@ZJ$QTE3pdge08u#yx)9z@w%=K z7qN2Rw7+#-w{G7KeqEf~J$;40s7clYAGO-uwY9aj78Vh)v9?xLRn^s>kN&V?bmL$G z`X`=elG&>J{d+3Bb*Fk&)!u0I_3`nz^X5&?(x8ipyVwmHQWZR&_yw{mYX*5oB=@|1 zo4ZvlGe0k{itE<>`_s$+Cow$ZWSOd8mz{mOc}=Or3xmb$*XJiD9yEV?^XAOs>#J=T z46h{aa#s)+7Y}UEKWRR<aaMd}<jWT?KAbhNuz2zDwH&_#Pw9+zf*woU82721n|q_; zq_NoF>_{E4>gwuLL1t!Vetv!{pYIH88+ceaSCq{7uz+ET;=!-R&z?P7byelhjrn@I zx<?;>eERh1thldWU;TTkCy{z(efrFm=gxo2a$3w28ML<TXMSB))~ahMdLJB5WteHN z*t=J@-OE04cBtO;LYZ%$J|$h~cz7n6^&jWl#feh{d);`g+&dCK_tx(Yx)my~-Ss>B z@`lRCHSysg)`xaT9DX)`-n=aROQ9@&*0F)VKRtT;>d~V`w;nH_SNEs%=A|Ty*0$BQ zA}`;+kB^TBsyxOm=_md~a`*a(vuEWyL!w!ojSNNF(lzoF#l&|V{V!v2+s?-3Nb$X# omEpmcm*LMlNV#gkI<7x#qC$*k9=<#D0F=c&UHx3vIVCg!03r$9EC2ui literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_phone_android@3x.png b/Telegram/Resources/icons/settings/devices/device_phone_android@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..710ba2fa98f0c3778a721c3278d7d43eca2f80ff GIT binary patch literal 1714 zcmb_ddo&Y%6yMBiM$L>#^Vp*Yk=M_5G^H)k?AIb<@>YaWC@M2DkACF+ns=i`$TN@S z@$)E@G>#&h%&Fv&QqfW-e)G@och2wc&bgoax!?P}=bm%#x!?Ouw4>T6DQGAF001TO zF{_gjj@p8}ltc#+O{OITynNDz1gPa97bFKOUmDr(_;G-q#FhsDZw3IQw?ZV)kN^Mx zv4H@Pgn?T=w$#73QEbqE_Ew-yF2WiBkVTTMh>q8QpNa_j7Vhf(Q<%rUV5(?kRzhzl zdlZJURnUlwvzv#L?o~QkVdJD!VIhgf;Bf3kR2giQ!sPzyniKEdd_FV8E4Xx(_fdaX zEtJP!`FnU}BRt&i`$w0ksK|5x_&3Ti9Vh~Ux3Gw$)7x8HS%roBh&^_LMMXtZQ&WY7 zJM$7#Qq17*USvsuKzJN(W1WH2)Rd8vbM;iV+P;1JX4{VkU0vERm`|q?MHK>pXv*wM z86S6|Xs+*(wePVe5@SR-{Zo(mv+v&@b^ks#cFNH)J7>3!oPDz#ZB2EqXk#NPjPJi) zQIVFY70LwC8b@!uYC>Z$hK7b?eec*5GP$AOE;O|9OH1qyTBG%pSkJ(~An==iT3VXx z83>8_#}Ew@1j%8u*|D)8GiF^~T}Oune^v*GYTmze*~_}6wsYCp$*D@2-PzcPo<w7% z?I+R=s#w#WpTUrRXJ=;$g)&A07~}C3TFL@0(UwAaRB4Vzlgx>hhYlS&c<{~Vg$3P1 z+TfM3sidSNKHo+3L?Afr;*uXR)7rGMvJ&5F$s_enTJGLmUR4$7@9)oGxN|Y^RL?8M zz!ye4Q1XtKuS6up0!2mBV<mZcP<b344<h>b_|WO%@$vDwxeB8mFGP&|=lS`=dRVK| zDClU{t!6ueC0$QX&oR5;>YJB&VsU%KNEYrSsx+7Yv43{)TzqEGOHCy5Tiktm1GHIL z>XJ*(z}n*C`BnKggpBzu*8ZYKrQidBd>u8DhD={$`6op9Xl_mb0aX+fa^$TrWAQqi z6TPsYb(O!mx>}<04GU82PWZHqT$!Mup&=>TqSVhx|3Y{2OP`?Zj`sFR{b0ZMF}rpG zGtb^_ZGENhF*_S$Ox`t#4mxw@&clwO=d$#c?z3%)%gdoEmUjvu?oVauMgb9TJg9jY z8E-`59hf?`KYOcK-uZjAq0st=4+A*MlGgqF`LnpVxV06JSxFfpZWWKkn`k?EPdr}k z%&)4hUYsmAL8ZQ=%zgd()xp7G+L}QClG6w6EwDP@g5Zh-QY1GcKVLUesip?`EC~z- z3(y1o{Y_aZX*zC&Dx_Y$avVzWQ44N-z*<dBt*IG|2@4CORwt#VhKm(q4#yFWL(F?_ zv_K}QXBd9<TrSNuCmgG*ORnSe^&R~biNQ!_Bf=bY4htGu^fmHzTM}_9D&|~S!^6V} z1R~<amy~LuL+wBa^;IFj{I3rO7uE4TLSw#1N?92iBZrvfl$U>Sxk?XEiP_3BqHa4v z@mUOchj_~4Q7dx1>{ll#O<GpcA8NoysD`%c+M1eSfkWf5rKP14Y?Nc!tG-D~0>Sm3 zoVcFFVu?t}$r!ixSR^Uk6qahf7Z@+!|4~>|S!qlwF;Sui6ciL>*O>j-xX0m0-;UjI zmaWLjkgm)&<fJ2E(9i=8m+ITv1WWg2qD@UrBl@`N11(55=M?Rn{U<ALlSmxZ@9X#W zaQ#o5I03nvKbF%d9-5xsTw3D2z2x>Bjh?xF?OOLmy$)P2cg|fG&stbmu#6@AD1&Cd zGxraY)t!%?NH{=#5AFOO47Z5{Gn|}q{ij*t%-m59Qe^pYk9X!^2Ge80>(euv^)6we z@O&McgCP&vYinsOnNeYJk9vEtXGK{|VIQ+5S>x>7YoRa~g_8L-Gs97=^LUy#+`wQk zJTk&<;EVA;;`46BD|=p7Xx||a2woGtbgTz8wq5!9pkZxoP4YC?KdZQvkDQ89jW^S` zsMb&18!eMX+qAbvDqRr?KG4E&Hm*=uTWau(v(_LEdSCT+aq$~g2?m217#K(mxbPcS c>;F@ATLyQknL3?b_ul$1$VaJGwIna*Ke?~j{r~^~ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_phone_ios.lottie b/Telegram/Resources/icons/settings/devices/device_phone_ios.lottie new file mode 100644 index 000000000..e5cefd536 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_phone_ios.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"iphone_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"apple","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[14.841,14.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":10,"s":[0,13.333,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":20,"s":[15,15,100]},{"t":30,"s":[0,13.333,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.31,0.445],[-0.161,0.375],[-0.056,0.176],[0.222,0.21],[0.009,0.704],[-0.829,0.5],[0.927,0.074],[0.494,-0.176],[0.093,0],[0.431,0.162],[0.264,0],[0.409,-0.246],[0.242,-0.418],[0,-0.737],[-0.236,-0.686],[-0.359,-0.515],[-0.213,-0.185],[-0.334,0.014],[-0.357,0.144],[-0.344,0.006],[-0.307,-0.133],[-0.25,0],[-0.306,0.278]],"o":[[0.234,-0.334],[0.07,-0.162],[-0.28,-0.122],[-0.5,-0.463],[-0.009,-0.899],[-0.463,-0.658],[-0.34,-0.028],[-0.524,0.19],[-0.12,0],[-0.431,-0.162],[-0.477,0.005],[-0.414,0.248],[-0.32,0.533],[0,0.644],[0.201,0.594],[0.32,0.449],[0.334,0.31],[0.222,-0.009],[0.317,-0.135],[0.335,0.007],[0.352,0.148],[0.347,-0.009],[0.195,-0.171]],"v":[[3.848,4.137],[4.441,3.071],[4.631,2.566],[3.871,2.066],[3.107,0.315],[4.334,-1.789],[2.249,-2.887],[0.998,-2.665],[0.076,-2.377],[-0.753,-2.623],[-1.8,-2.868],[-3.153,-2.484],[-4.154,-1.469],[-4.631,0.435],[-4.274,2.427],[-3.431,4.1],[-2.634,5.054],[-1.633,5.499],[-0.767,5.272],[0.234,5.059],[1.207,5.272],[2.11,5.49],[3.088,5.059]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-0.482,0.565],[0,0.551],[0.005,0.074],[0.277,-0.142],[0.217,-0.246],[0,-0.528],[-0.01,-0.069]],"o":[[0.403,-0.473],[0,-0.074],[-0.31,0.023],[-0.297,0.139],[-0.408,0.463],[0,0.07],[0.63,0.051]],"v":[[1.717,-3.739],[2.319,-5.278],[2.31,-5.5],[1.42,-5.25],[0.642,-4.666],[0.002,-3.109],[0.016,-2.901]],"c":true},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"apple","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Vector 39","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,6,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,-0.884],[0.221,0],[0,0],[0,0.221],[0.884,0],[0,0]],"o":[[0,0],[-0.884,0],[0,0.221],[0,0],[-0.221,0],[0,-0.884],[0,0],[0,0]],"v":[[4,-1],[3.6,-1],[2,0.6],[1.6,1],[-1.6,1],[-2,0.6],[-3.6,-1],[-4,-1]],"c":false}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":7,"s":[{"i":[[0,0],[0,0],[0,-0.884],[0.028,0],[0,0],[0,0.221],[0.11,0],[0,0]],"o":[[0,0],[-0.11,0],[0,0.221],[0,0],[-0.028,0],[0,-0.884],[0,0],[0,0]],"v":[[0.5,-1],[0.45,-1],[0.25,0.6],[0.2,1],[-0.2,1],[-0.25,0.6],[-0.45,-1],[-0.5,-1]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":33,"s":[{"i":[[0,0],[0,0],[0,-0.884],[0.028,0],[0,0],[0,0.221],[0.11,0],[0,0]],"o":[[0,0],[-0.11,0],[0,0.221],[0,0],[-0.028,0],[0,-0.884],[0,0],[0,0]],"v":[[0.5,-1],[0.45,-1],[0.25,0.6],[0.2,1],[-0.2,1],[-0.25,0.6],[-0.45,-1],[-0.5,-1]],"c":false}]},{"t":40,"s":[{"i":[[0,0],[0,0],[0,-0.884],[0.221,0],[0,0],[0,0.221],[0.884,0],[0,0]],"o":[[0,0],[-0.884,0],[0,0.221],[0,0],[-0.221,0],[0,-0.884],[0,0],[0,0]],"v":[[4,-1],[3.6,-1],[2,0.6],[1.6,1],[-1.6,1],[-2,0.6],[-3.6,-1],[-4,-1]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Vector 39","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Rectangle 21","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[-0.218,-0.428],[-0.376,-0.192],[-1.12,0],[0,0],[-0.428,0.218],[-0.192,0.376],[0,1.12],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,1.12],[0.192,0.376],[0.428,0.218],[0,0],[1.12,0],[0.376,-0.192],[0.218,-0.428],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-5.782,-8.908],[-6,-6.8],[-6,6.8],[-5.782,8.908],[-4.908,9.782],[-2.8,10],[2.8,10],[4.908,9.782],[5.782,8.908],[6,6.8],[6,-6.8],[5.782,-8.908],[4.908,-9.782],[2.8,-10],[-2.8,-10],[-4.908,-9.782]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0.016,-0.376],[0,-1.12],[0,0],[-0.018,-0.428],[-0.031,-0.192],[-0.093,0],[0,0],[-0.036,0.218],[-0.016,0.376],[0,1.12],[0,0],[0.018,0.428],[0.031,0.192],[0.093,0],[0,0],[0.036,-0.218]],"o":[[-0.018,0.428],[0,0],[0,1.12],[0.016,0.376],[0.036,0.218],[0,0],[0.093,0],[0.031,-0.192],[0.018,-0.428],[0,0],[0,-1.12],[-0.016,-0.376],[-0.036,-0.218],[0,0],[-0.093,0],[-0.031,0.192]],"v":[[-0.482,-8.908],[-0.5,-6.8],[-0.5,6.8],[-0.482,8.908],[-0.409,9.782],[-0.233,10],[0.233,10],[0.409,9.782],[0.482,8.908],[0.5,6.8],[0.5,-6.8],[0.482,-8.908],[0.409,-9.782],[0.233,-10],[-0.233,-10],[-0.409,-9.782]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[-0.218,-0.428],[-0.376,-0.192],[-1.12,0],[0,0],[-0.428,0.218],[-0.192,0.376],[0,1.12],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,1.12],[0.192,0.376],[0.428,0.218],[0,0],[1.12,0],[0.376,-0.192],[0.218,-0.428],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-5.782,-8.908],[-6,-6.8],[-6,6.8],[-5.782,8.908],[-4.908,9.782],[-2.8,10],[2.8,10],[4.908,9.782],[5.782,8.908],[6,6.8],[6,-6.8],[5.782,-8.908],[4.908,-9.782],[2.8,-10],[-2.8,-10],[-4.908,-9.782]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0.016,-0.376],[0,-1.12],[0,0],[-0.018,-0.428],[-0.031,-0.192],[-0.093,0],[0,0],[-0.036,0.218],[-0.016,0.376],[0,1.12],[0,0],[0.018,0.428],[0.031,0.192],[0.093,0],[0,0],[0.036,-0.218]],"o":[[-0.018,0.428],[0,0],[0,1.12],[0.016,0.376],[0.036,0.218],[0,0],[0.093,0],[0.031,-0.192],[0.018,-0.428],[0,0],[0,-1.12],[-0.016,-0.376],[-0.036,-0.218],[0,0],[-0.093,0],[-0.031,0.192]],"v":[[-0.482,-8.908],[-0.5,-6.8],[-0.5,6.8],[-0.482,8.908],[-0.409,9.782],[-0.233,10],[0.233,10],[0.409,9.782],[0.482,8.908],[0.5,6.8],[0.5,-6.8],[0.482,-8.908],[0.409,-9.782],[0.233,-10],[-0.233,-10],[-0.409,-9.782]],"c":true}]},{"t":40,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[-0.218,-0.428],[-0.376,-0.192],[-1.12,0],[0,0],[-0.428,0.218],[-0.192,0.376],[0,1.12],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,1.12],[0.192,0.376],[0.428,0.218],[0,0],[1.12,0],[0.376,-0.192],[0.218,-0.428],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-5.782,-8.908],[-6,-6.8],[-6,6.8],[-5.782,8.908],[-4.908,9.782],[-2.8,10],[2.8,10],[4.908,9.782],[5.782,8.908],[6,6.8],[6,-6.8],[5.782,-8.908],[4.908,-9.782],[2.8,-10],[-2.8,-10],[-4.908,-9.782]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"rd","nm":"Скругленные углы 1","r":{"a":0,"k":2,"ix":1},"ix":2,"mn":"ADBE Vector Filter - RC","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[100]},{"t":30,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle 21","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_phone_ios.png b/Telegram/Resources/icons/settings/devices/device_phone_ios.png new file mode 100644 index 0000000000000000000000000000000000000000..9c67d38a1896938dda4460a23c61cc119486eabc GIT binary patch literal 636 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&EF#PZ!6K2+p@* zJ69cY5GiHce(X?`qd?MPrz`ORN8C1gOy(EJ%@GNTj99wqGxHa2rm(Jv&IHLVf!tr2 zlM)}Ud;f{;lbqPOvq64cil-y<{M3q%KTKfvZcsfSWH8x5L12PPrrGSLpDT@PwC7aV z?2FO6zu#SL@<yf98LvP8G@H%)(N2a>Z~EyCEuv1l@2cH1;A%>6J;hVLH?H~P-@1IW z*=aw-EN3nAT^>B|x$dikmwDT*#kvoM{8wl>{4mE%nupE)-R&~Y%~d8+bN$pOeM>P~ z8MeCBY2od+dE29nnC1G9*Vu`>UNj3=3R!(MZ@cuz13h#7j&I`%S}EeA*Q0&VVd2-R zyJo*XR`h&lOIdtz!`a*KzsIi+-}AR_f4S9MkLq7aQ~j3P%JB#BC8%hr%*%WKwQ6D% zOPSsLj}^OYRr;qhrx?vNX<6Q&JdxwyVdhUiYi`9f-n%@Zbj2zuu2zlpt@U?af1Pm5 zDrfucr$v@~j+(0+FBDj**K4CW)$6_suPxC19^p*UYr}r*pC9LGB4sPr|AXt)Q=`}` z0!*`{KM9}Ml;a`EowqgWKXb!1CdJ8OYwm}Ou<n0+Snpg_;{?a0L5;rW3M^uD#HOES zHB;zxSrnmjh>P*;zWeX@$LY(S_lscCVVuU`{XoA_{$%P#9({e$&7hd~boFyt=akR{ E026=pF#rGn literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_phone_ios@2x.png b/Telegram/Resources/icons/settings/devices/device_phone_ios@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b96ea8ae597d050a9bff5eb674565f9a924cbb GIT binary patch literal 1263 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an^x>fhE$@#WAFU z@om&?ZQVeL?c1&STc+5YSXv?AukrNTqsBY(Q>LWHomlLoSviS=KW<4yk?$YIIZGxK zX}jjR>OHO7Klk(L%>J3So1ee=R&zOWS-#x2xYx7uYd`<~d+p4ut@}*l;%YW9tzpnU zz=efMh-o}?=FGo;|DHa5x^A7`<BN4ZEo;`S*}8S>{l3M^mmd##Q5L*-xBQ*F?d9d= z=g*#9x@3vRS~)qnC7V*Y>+0$rKYrW?)U2<eu|X%jQzGwu{;|GGj~^!|BrJH6eAX#2 zaANcNNBg&Lzdox)HOy>wZgR4-<FD04wY9Q>f{HsMHwQ#StVrYY4h@}Z%6U5S>({R< zPnw&XpRU!3zIW%&l*#qg)vMR9->ws_p7i|rbD>I~{7suT&y`sdV{2z;XKO1fExmfp znj<#HPldQXPBWGc00Ji`r<xj@dC%Xzef#(C-~N97x!uQ(99gn-X={5sJ3G6)j7&~$ z?%Q|oUYTF){8sN*SzR3-9)AAZxqbWgdHI`i0XfRb%D}(?`bx-X&)bcY4omUbJUv%` zIz2so?%cVao}QETUufO4$7XqAj4$Wq85b{JT()f4!-t6%f~_qg3m$LTy7lL;UtDMR zY}@wj@87$eR-vVJb#Zq%>tkbOMg7AImn~oZ(TDT;e@RJ6eSQ6P4wG*bel|BT;W#eS zeC^aJE>7ogM+4<kCr=9YSu#wWI&~+LZ_@GGe4cuOGnp@+Im5&08SZFhWyO*zCnS{g z>T;j-y`JeZXG^Srfl+DkxhgFyi_2NIvPSCD$&)9aoOr^huCD$xbd?eB&mB7~v>av? z^6~SVzGdq*d9ty}u)}%3C7Vbs`}uR1Ch&7`e0cLIZ<|r3!$QUS;^N0@yyx$1c=mbC z!a8lKPvYX@zor{jn3<bjPhdIw%;d?$(hEI)sYia<TUpK0k}8yWqql3;%)pz#0Pb`Q zZvFi6<H2iEqW)(sX53Ex>A*U}f^XW*SqnIOPjrZiiwi$eJI$q_9v&6t)!`${W!cl! z#ie34ecCjImRk?^M;<zKXx_Ye9KR#gMS}jG;E-PKWqgbI_|vDVmo^<^+$qd>e*ew) z;^he%H5>L^e5rRRSXWoKi<>*>;4xuE&$9vFzki=NabnYl&;uR4y|s09*I0a0bMo`U zV}e$29SHmwa{S;4At@;>k$6^#H3!1N!>{h})G;+R?ds-?@bvLHqF$ns^VCsIO^rom zH6!QmxAW)EFM7dt`o`fup7S+K&CH}!bWDMMK6Gts+p%LlDVL*7dyXD;)v4HUx#6tL z*GQd~x>{LoZf;<xpYTcUTT#}WnxC(4Z!f=pR(h-1qvXwV=gO|+H;m!u<()cZ$_n#n zL-(xQ+@seyCVjqp_wIoM4cdV{m#@7Le(ZQvFeo_q;KOg<%4%!>&X@eLVui*My$$Dk v-wW5@Fqh1FmXpXD!5G#cnt+QsFZzQyM&nnfZ^zVBP+{Zg>gTe~DWM4f-Xkfx literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_phone_ios@3x.png b/Telegram/Resources/icons/settings/devices/device_phone_ios@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..935622b5eaf683589a5e33e6ee8e160f5af2304e GIT binary patch literal 1843 zcmb7FdpHvc8{aIGu`@e_BP6**#)&#-mCQyHs+G%35+b+Fwi98>Tr-z!l^AkM>6kGU z=5`{v70TtDjYEthESDxEP4caOzVE;9`M&pge(&$S{qw%h^S*D|*)tAMh!z9@06-m2 z+Plb@{vA8zWI7CODUuN|+{FO{;Jwj)FLUfK6C49ip9Yx9?41B0BLuMHdx;EMG5`Re z5+DF1W8n8#iQK=l^b*j2?C*toIT~0109@#3k9La&E){y6RP)j9K8&!SDLNgr>pax1 zOqondPuG{flS}w<zkcatGt6eAWJ5pL#voZgdT&!acrPT8pr*B-@DBod4&CV-7{UqW zY;SAu@t%=EQDas?Z~DHimo*lADU?X8y7R1BMMsPk6r^yVzU_`w09;SbK7Oal|Lpi4 z06KU<Q)9Op!{<J{ZOqEe)zs0^QBhG*S2yBcJYBA&s7Tvbo?=`0eB0bShC~h|2gx0q znx8-K?F}7>%iI3)d9J@$j*D-tZD?rt`)^N>xXD}FqcyHHhPt`FR9L9<;O%eAof#UX z5RXQl0!;U-^i!`|ly-lAzei&RriYc8IW7{hC+QIp@202sJ&Zh@v9i8ibnjl)B-525 zYH_d0C=I0$iCMGE8u4g`sZU)K#?B4~gOv?NMMhQ#Q3*Hd`Fy8v(b!mX%%_Uyy5+8; zH<UE!+uslgx|HF?Mbg!y#{BupN;Aa|NOwHmh_6uLK0Gj>m(cR)5w@;K(c*eQK*#<V zYtib;N;11TFag`Vs;a8G@xrCXD|DcR&yQ>pSeToutE(3s3CBW5bqd_?58Zva2SqP| zB?T`p3|C)^iJAC=W{0M^R*I2umH2pSqo;<3MhE!TM7yj_il5)q>Ri8-l@*uE#ZN($ zl}|Z4XQZb?AP`K7I!s?PFF#+p_WsYcwL{0*XMFX!49SC_{-UXmvC`KZ5thl}@qRe> zb6S0QR+ejM==^jy>$G7-gm9T091?OrJ|7gFos&Z*k;Ep>6f*=uAQX0WbRds@`;zr( zZLP1bk3=F}@bO`{2q(?*x2wGB&wOb04(sghX0ceiRi03zh(vC6wPd_GYUq_ojW9ny z-`d)G5z-8Di5cP;*8D}-^&B+clYL+vvnQcydfFeqNUBew_;#S{a42Kr?npu(LRne4 zB(ChTR5LcHVt0GNqZ#mCPD2Bs_w69!;4X5lw(9B~@uGr)0x|E{_2#&3n{etcTH3n0 zEzuuKyBZ%Tv3Rwa4@WNApitGZAA<3Lt2EM+{JIn>CLrLU%TggRcyr>}HK#8jwG&cl z%=Lkvj+b$gQvJdxlzU-mZy_Wyxd;_^5#90ieC(AgPuXmCB2UY0d}5-dxtU14HD``m zOBJm|^pU)A#Wsh%utK@71EN1@u6SIfC39eGWF*&}{j&w>cX68z++g-lmE}v=v$O4E z9(a7^3j+r~x_Eqie1<wHBAIR`3PSj<Yx=byy12jS_$CG6#^jp&w3snGxug}l?WM6_ zxp!ZkC$w!OOjyvxw>N~t6&qkoARG?w>6v8chR<eS6_i8<^e8i_6Eh6mBqVav$Xn-% zOHT`Z<No&YRMH`(H$hL%o<yU8b57ZuAMD5tCkzplJFAPG+2$rmNkLU)GI_}LjoG_S zJ|QZqzNV(hp~)*km64J%lIVX4S`)hKz9ax{g}pIQ=8VGy1>DTb+x;u~LMwes!1ZXj z-FGuRP&`^kJT=PE#>QN`wzai&VqcdFtZVockeT*cB5L(VmOk~g%c+Etve|5_HiP2Z zgM;TluPv8W7KRtJgURHshxoh#zf1UDqNlHraXn0k?bVDhcXMJm`X4=*IK{(2B{s43 zMb@+{`fgcX{lzHM(qrV<XmKF(eQfkU8Fm+be>c<1w%&bo7-#y-Uo$1eafgYCiF98& z#aDO)i;mlpy8mYQRLS-;jYjh<NMXtU74X@!0AgurxdY8IF^mSM+odq>IIwinY+K9A zQyKLbL4vUjIMKwpmCJSBZ}hc8kD3Z}bQ*N%8W|pD7uS~xk_B6@dG03N&)N_;C#N4X z!Ia}CU3!5YFN`jM*PCq<+m@;-D(>iYuB*9Z=Z;*~i(lmt7N#cSyvobVZxJF-I4BgO zWTWZc{0WruvE6O2vW2Zg1qZ7v_0Q2$zuXoR|FS0^5#l5Nn64766*e8cM~|J%EU~A; z_eo6;956OE&OHCTO(x6BRCsQ>VE(H8<5TvIj;~}JcMvY`tme06+_$<QkvQCxkLtvP zijgz3vqlC6zG#0<G;UV5?Xqi3=<dWQx!wFfuKIub1dFN9x9PVP?#2!gnq>b9;CSMU JJrCoX_%CKoN5=pF literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_tablet_android.png b/Telegram/Resources/icons/settings/devices/device_tablet_android.png new file mode 100644 index 0000000000000000000000000000000000000000..a88ae751ca7e4cb98b1af87bf459feb7c1cac53a GIT binary patch literal 592 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&EFwPZ!6K2+p@* z-d%?r1WXTWq)cCH&1$bw9bR`pI7PhR<tgnW#udEbhAcKtN)^n74-c(X4-h^&lbv^= z=aqHyS|lZ`xL;O%T@`ozwaT)f!0AN}mJ4KlgtD7-MeV+uXEs|T?UbclfBRuY-uit7 zGS5F&)Of~eI4%tM>U?5DZuDgjwU289nw;b+cZr=2Uww7?RHq3+8q>s2i75V2{`vc_ z-s_exUoO8a*%|XJmEm04W}7&Xk2d}FFU)59F230D_(#>=M<<W9vN|<*eDo3F+G^k0 z!;@$rQDQZ>ZvXz<Z}p~gZ`!pkdfL^uWfM<L@a8bSygp3(#nazRE?#9-J2&6Y+dlau z%l3%XS09x(2q*+8e5l~euB*Cia<R~E{^Pk(>#s+a>lm0gv?wfLn^N;FQR3bA>Wm-z z_<vr0dF08)f>?77rWOU4KohCnh*d53OMV?qa?E6xGHx*1d*tT)^XXkRlT<{c9(Ty^ z`SPBRumAYt@YxK!&eClcUw=I{#Xb1iy0F!}?ao!%T)~HGysRVpCe^>c@wTkmzhdfA zk*>4x>#x7O``t^ke=$pIrA_mMeTx4-Ia_az7u8~)!hEVRiLZwFzmw_(HknjcP;7d- L`njxgN@xNA0Xgae literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_tablet_android@2x.png b/Telegram/Resources/icons/settings/devices/device_tablet_android@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..44a46603448f5201600dbbb6d53ed61a98d52b38 GIT binary patch literal 1065 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an=E-e6pvDV@L(# z+bCaeqd*B?&c&_^CpHP_6+JxH+p^m1A^(Js3sGOhZ|%xldO@lDA4hz^k@*5Fj^5%; zKO5aT9H*=m3asLPf1p6pefPOL)_r^WSN|?N_u1yTP1W3sH*YNxw{+l}!2E;(JC#t) zF0)9Br~UBD5-SNFE15${Sy{h-{si)t2Ca-+pS|sx<=GUY$tPKkMt*QoS`xiH%Iy2U zfB&vupU%4Js)$GXk{sS+$NZ8_ripgCv`jg4EXK~wN+q`M;Z0G6e;+<fP@f#pGhqqS zk?zpcR1=~6rMJsYxz61lY`$hqs}-1+>iXQ?-adS4PQ#`wt_MrI^{1bXTmSvtyT1K9 zoE9>8ZH;=mT*BE){C(N(^v#iW^PlIaCG3pho3eMWZStfak(Z7hPujS7^X7{gCDqlZ zgT)dUnwgaN>(0&y(OP;h|0UnNdGi7uy?giW_3PD4FTVfg=jX5gx7VPevNG@*BLh!D zK#B9pZjY?EZ`*zf?02)UwDeRFn(FoR*Dos(u7@HSX0xTlu1!9va&7<q{fum`%o|>Q zxqfbOY=3|MpR3Gk$|^28csyTP|7_vTt68zJu^+qHB}D4>BrYlW`2G9$iy2>j{+zkr zrNNPL;`}5Y&2SsOZ51}>eySelVqkT-aMSf+a_6JvmtP(|%DQj+ltn-@H{X2o^5sh< z-WyB|2CNK9FE}=F<ad8{WtaPXbl>L9#u7XQRS#dW+83R_-s_sf$RKe*i@~wvyiK%x zML>{_SSYXE{PXX>C%-DO67M`N#?ZjHMo5rl;gywd*>=rce>H2CQ{mb^xox-KHa9b$ z6<Zc@M2z9{^e-Hy_iGgA&eu<VDjNIo`Sa(q9Db;?&rhFzdTC_%@*7ME|M=xwCaJ_- z6OCJZF(6cvnd3#-?pdy7UHg+t7(XwJm^O3cl*@8f)qCg7+!Uebq^Pi4DND!oPl1I@ z|M5j$E-SWgHxImc!y@R&W4U{eKW@l8)5F}@@J=#QZP}rzpJpD<)y(rNIQ;A5$3|wK z;x^qXer*F$9*JY_?(Wm4PxtQgG#2v^>~AXQt|~50US`C-Bk0~Yu`9YNtexUXkJ;E3 zp36_T+%m;yE;3JN!RPny-#d9lYMzLi^eSY>PJYE#9fljWEbO1pRqgTPf9;1G2WGcw h;K~h<oU)&}o>7VS&WW<QlD(j8>gnp|vd$@?2>|@Q%uWCR literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_tablet_android@3x.png b/Telegram/Resources/icons/settings/devices/device_tablet_android@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..596ae04258945541aba915b8a9c38e1a624726c8 GIT binary patch literal 1557 zcmeAS@N?(olHy`uVBq!ia0vp^bs)^a1SE4EjqU>}#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1ATfmH9gA^)E6L(`^V6FFbaSW+o zd^^`yyEI<nnEd)cHZ?b&o))F0ds=){M1+lJeqijG!msdCgiRxm?G$@}o0rSPC#s6h zU5uU2>;Fcdk6ioh_U7+Vzt?Qt{@lnS{cZK#>wiDpe!b`I-t!+{i~sj%V4(#4Q@?QI z#*R&!ipt8qef|2?+PGeWO?9Ecl<&shFF$$m<n`<9v<7yOrcVJ+{1^ALPAz<w6u#iZ z{B7Sp7Vt%+%`{mUV$Hq(ir=hv@A9lW&uos+-g;!uH4DKHL19r@ndZk=svmm(`}giO zYjh4NuGRE@@$t%}u%lv+rcAkUU{e3o494w$MO^ad|D50B;{W9whlTf<P~0>Zqs)O_ zd-jCzxE)@-TARg$YhrfcALY;a`T29_&dn&RtFzmub*4Bmk#XU!UAqL#&K%D_Z<>|= z@!z*YI_~c7PBD>@mDSbK&%}=NSvGZKsH>|NsvJ5VUAu1k<L$ium79{Ps;nknc+9i2 zp{-%w$`@y27Jn0yzq=~EEI;4>MPb>~FJG=0OBV|}RTi-^Ca-Flz5Q2AzWOVfbIQuf zi|%jSxN%9;L;i&w4FR6YHym>xm+)9Qe(wBrFu2&=y*=RV`}g0!e&w``(>;Iwyu6&8 zoV<J?ce1myb8W3{UUXDcR#sMvv<fpbvqbX4$43|M-5Yx&`Q9x{Z_&*X!P}R-=KB{M z9ITX`pa1^!>C?Av-8yo_B{VcNLUTqF^PLAgodwPUy;l-7i!!gj`2XzLvPFxWN}}W9 z<o4&j<gskZSkgCNn|+?t-^WUA|NhlYzNg?0RITP|yxjMe{k&7`?CfW6I|#IR&AMxo z-=?IJC8xin;;GN-surgMF04zYS_v0ED)0MlR$5y6QmnV(kIC=pu^+yCY1!WSvPk@* zfY`zRllB?J_At#ks$(Kr`v2!oO@2j@#TVwylUvfUYJov`&zo~eVzat-#mB{cyKZi0 zw{Q3E-Fx@y-e2{*HiR4Kc+Qqpu{{%&Ry7@(6F(!_d*R9pzkx~a|M8^{A0}%4`0*nn zH@DY~fqjQ3=M%aAuU=)TOyWyq?$^3MJJHwc_n9+2`wyQz>-)0d;H68G?z?Y2;P|yY zX0GVtn>Qn`su#Qo7d<?wQ@G{(_wN=A5AykALKde7&*hR~|FwSII;GeJ|IeGS>FH(0 z#KiFARA*#({MxgBzqz?N`z}vqnFngB6HaTEHk4daFIZspyiVrELX&M<x3-pcB=5Jj zwvLXDwm2#NZ1Js!Z0R0u?(U(xt=z0;TEUbWSXKT|&UEoCE7)DX&fdmm%jH$Gj(P@d zW$3xP*xt_W+m)2W%br30@$W7^{=9d&8ux6!Nmaj!`v3j>c{6x&tFOAJZS79?_nIfp zEwh=Nsy_F$j$pnzlf94V*035~vtOSkEJ~Yx(74d`(6M93e*OA&_pWT8&Lb(N7=Z^n z)flS%LUh<SF_tQXyjnZwXzKI}*REYNGc`SX_UywVcFU*o6`Ov`25n4Xs@0miMB)hF zulg@L?T-FkBHQ(1>&2N|o%g0J*fx(6rtZS?7uSnKXkQij)!of~*)_>Ke{G2O3w}Pn zoQ1|`Coc|4-g-(tu)lSshu@#;jIK=5+B(<l>SX2aKfnCjD*H$(uUas$1p51=euiN7 h^9!966>cF+{~4L89gCv3f9C-e$)2u$F6*2UngH3Q!7cy* literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_tablet_ios.lottie b/Telegram/Resources/icons/settings/devices/device_tablet_ios.lottie new file mode 100644 index 000000000..8817d76b4 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_tablet_ios.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"ipad_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"apple","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[14.841,14.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":10,"s":[0,13.333,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[1,1,0.333],"y":[0,0,0]},"t":20,"s":[15,15,100]},{"t":30,"s":[0,13.333,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.31,0.445],[-0.161,0.375],[-0.056,0.176],[0.222,0.21],[0.009,0.704],[-0.829,0.5],[0.927,0.074],[0.494,-0.176],[0.093,0],[0.431,0.162],[0.264,0],[0.409,-0.246],[0.242,-0.418],[0,-0.737],[-0.236,-0.686],[-0.359,-0.515],[-0.213,-0.185],[-0.334,0.014],[-0.357,0.144],[-0.344,0.006],[-0.307,-0.133],[-0.25,0],[-0.306,0.278]],"o":[[0.234,-0.334],[0.07,-0.162],[-0.28,-0.122],[-0.5,-0.463],[-0.009,-0.899],[-0.463,-0.658],[-0.34,-0.028],[-0.524,0.19],[-0.12,0],[-0.431,-0.162],[-0.477,0.005],[-0.414,0.248],[-0.32,0.533],[0,0.644],[0.201,0.594],[0.32,0.449],[0.334,0.31],[0.222,-0.009],[0.317,-0.135],[0.335,0.007],[0.352,0.148],[0.347,-0.009],[0.195,-0.171]],"v":[[3.848,4.137],[4.441,3.071],[4.631,2.566],[3.871,2.066],[3.107,0.315],[4.334,-1.789],[2.249,-2.887],[0.998,-2.665],[0.076,-2.377],[-0.753,-2.623],[-1.8,-2.868],[-3.153,-2.484],[-4.154,-1.469],[-4.631,0.435],[-4.274,2.427],[-3.431,4.1],[-2.634,5.054],[-1.633,5.499],[-0.767,5.272],[0.234,5.059],[1.207,5.272],[2.11,5.49],[3.088,5.059]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[-0.482,0.565],[0,0.551],[0.005,0.074],[0.277,-0.142],[0.217,-0.246],[0,-0.528],[-0.01,-0.069]],"o":[[0.403,-0.473],[0,-0.074],[-0.31,0.023],[-0.297,0.139],[-0.408,0.463],[0,0.07],[0.63,0.051]],"v":[[1.717,-3.739],[2.319,-5.278],[2.31,-5.5],[1.42,-5.25],[0.642,-4.666],[0.002,-3.109],[0.016,-2.901]],"c":true},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"apple","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Rectangle 21","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[-0.218,-0.428],[-0.376,-0.192],[-1.12,0],[0,0],[-0.428,0.218],[-0.192,0.376],[0,1.12],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,1.12],[0.192,0.376],[0.428,0.218],[0,0],[1.12,0],[0.376,-0.192],[0.218,-0.428],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-7.782,-8.908],[-8,-6.8],[-8,6.8],[-7.782,8.908],[-6.908,9.782],[-4.8,10],[4.8,10],[6.908,9.782],[7.782,8.908],[8,6.8],[8,-6.8],[7.782,-8.908],[6.908,-9.782],[4.8,-10],[-4.8,-10],[-6.908,-9.782]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0.012,-0.376],[0,-1.12],[0,0],[-0.014,-0.428],[-0.024,-0.192],[-0.07,0],[0,0],[-0.027,0.218],[-0.012,0.376],[0,1.12],[0,0],[0.014,0.428],[0.024,0.192],[0.07,0],[0,0],[0.027,-0.218]],"o":[[-0.014,0.428],[0,0],[0,1.12],[0.012,0.376],[0.027,0.218],[0,0],[0.07,0],[0.024,-0.192],[0.014,-0.428],[0,0],[0,-1.12],[-0.012,-0.376],[-0.027,-0.218],[0,0],[-0.07,0],[-0.024,0.192]],"v":[[-0.486,-8.908],[-0.5,-6.8],[-0.5,6.8],[-0.486,8.908],[-0.432,9.782],[-0.3,10],[0.3,10],[0.432,9.782],[0.486,8.908],[0.5,6.8],[0.5,-6.8],[0.486,-8.908],[0.432,-9.782],[0.3,-10],[-0.3,-10],[-0.432,-9.782]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":1,"y":0},"t":20,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[-0.218,-0.428],[-0.376,-0.192],[-1.12,0],[0,0],[-0.428,0.218],[-0.192,0.376],[0,1.12],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,1.12],[0.192,0.376],[0.428,0.218],[0,0],[1.12,0],[0.376,-0.192],[0.218,-0.428],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-7.782,-8.908],[-8,-6.8],[-8,6.8],[-7.782,8.908],[-6.908,9.782],[-4.8,10],[4.8,10],[6.908,9.782],[7.782,8.908],[8,6.8],[8,-6.8],[7.782,-8.908],[6.908,-9.782],[4.8,-10],[-4.8,-10],[-6.908,-9.782]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":30,"s":[{"i":[[0.012,-0.376],[0,-1.12],[0,0],[-0.014,-0.428],[-0.024,-0.192],[-0.07,0],[0,0],[-0.027,0.218],[-0.012,0.376],[0,1.12],[0,0],[0.014,0.428],[0.024,0.192],[0.07,0],[0,0],[0.027,-0.218]],"o":[[-0.014,0.428],[0,0],[0,1.12],[0.012,0.376],[0.027,0.218],[0,0],[0.07,0],[0.024,-0.192],[0.014,-0.428],[0,0],[0,-1.12],[-0.012,-0.376],[-0.027,-0.218],[0,0],[-0.07,0],[-0.024,0.192]],"v":[[-0.486,-8.908],[-0.5,-6.8],[-0.5,6.8],[-0.486,8.908],[-0.432,9.782],[-0.3,10],[0.3,10],[0.432,9.782],[0.486,8.908],[0.5,6.8],[0.5,-6.8],[0.486,-8.908],[0.432,-9.782],[0.3,-10],[-0.3,-10],[-0.432,-9.782]],"c":true}]},{"t":40,"s":[{"i":[[0.192,-0.376],[0,-1.12],[0,0],[-0.218,-0.428],[-0.376,-0.192],[-1.12,0],[0,0],[-0.428,0.218],[-0.192,0.376],[0,1.12],[0,0],[0.218,0.428],[0.376,0.192],[1.12,0],[0,0],[0.428,-0.218]],"o":[[-0.218,0.428],[0,0],[0,1.12],[0.192,0.376],[0.428,0.218],[0,0],[1.12,0],[0.376,-0.192],[0.218,-0.428],[0,0],[0,-1.12],[-0.192,-0.376],[-0.428,-0.218],[0,0],[-1.12,0],[-0.376,0.192]],"v":[[-7.782,-8.908],[-8,-6.8],[-8,6.8],[-7.782,8.908],[-6.908,9.782],[-4.8,10],[4.8,10],[6.908,9.782],[7.782,8.908],[8,6.8],[8,-6.8],[7.782,-8.908],[6.908,-9.782],[4.8,-10],[-4.8,-10],[-6.908,-9.782]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[100]},{"t":30,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Rectangle 21","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-120,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_tablet_ios.png b/Telegram/Resources/icons/settings/devices/device_tablet_ios.png new file mode 100644 index 0000000000000000000000000000000000000000..09fbacf4c76319745a69f8f6abe914eff2ede54d GIT binary patch literal 584 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&EFoPZ!6K2+p@* zw*5^G0!KgdFji^*XE5bGDCDgzw04Wp!T$;A&*KkVxFDI#v35VBlcUd@H99)Je>8IT ztmHYgYqR#f*v%p-<<HJmpH5caJxN9GVMBp~#fRm5S0b)Nt(~^+^;Rii36A&Qf0ymP z`~JJm6OLt<U*5?xPk2(bfQ3si%k!6$;NgcGE@mv)z{KFi*6gSlG4Ii*u0>N$M<}gY zwa6ewZs`i99}R!9wnnYL{$6*{sy``4FTYl8xOi{!@zv8#KV5BGZ#Vz-*IyYXN=yp` zQvbUNJ}tZdw#-o=W_|c!gS5>zKUT=}AK#Yy{det@wG0QIK0N)&G;-S3+qpm9)d(E> z{8Q%M9_<JJzF&U1WexA4zQ+gDG}ds=EjqnC(@W)T+3u(JU0j|u-}$sj=lN^-$7j<% zOKj%f@znRS%BJdh+aq*3G`%K7-z$w(y8E<<^YDZji&%C()jh2=Pj%L^gX@-P2nJU? zo@y@g<;KgBuE!F;_T9`;^DMHEskz^7%^27$bmS>_Eq_b_n_HlZVyb`P?DKwSn?yd9 zOn;EEkU1{(zFv*epR23YF5mrWuYb!wtfLbctO_zKULD}Sd0Eie{^W<rpjh;D^>bP0 Hl+XkKLWB6Z literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_tablet_ios@2x.png b/Telegram/Resources/icons/settings/devices/device_tablet_ios@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2af837f4c515866d528d78e2890f1c3937bad2ca GIT binary patch literal 1052 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an=E-yv@_aF{Fa= zZIrD?QKHB@f8QlW8J%?o8WSCaOSz&JDc<UyB_t&k+qA@Y?i^>Ss4Jl|BJHzUGFGr? zD|u)wHOvT{pmg-T^@HQ?_OJFnzxR9Jcemr7_p9sXJ-2*cYx}%t_pTStToag<Flahp zr!wLi^Og$q_4MTA<()fw_UrfW+M=Jm=WO4;{q^hD5jx*Kefsot(ky#hx$~btf0oyi z_h5N>CFQqad3=2Q!hi=(f8r<Y-fiviORzcXe&XuSDfiB>dOj6?H&H$*EzPY?AgAT! z%gi;W%d1&Wl$UFlWz`+Op0{+lJL40smW+1)->+Tbew!vW2RKjNzjNox>#uV%w)L&z z{4~)crKaZ2!-6YWTdx`1T(jzqmWfpF*RNk)lo;PETehsnO<8)@+QqA4el|BZs}(zJ z-@5gx)Z%I3K(Ms5;%ZiDLBR%3-dkU<Ul0FSsIaoe_sW_-M~}KL4PxYzmX<z#{P>$= z2g{5;s{Q}+rKGGZOm8})z?9nR>fbM3Xvp>3+u5ZV%}m;OgKve~^`kkzzkmO}ckkYf z8#k)8-@AA3=+UEp|NhM|;d=Mq+&*S&gwDbM4!%R@wl^r<j$?FWJ$ZOD`}EVTr?za{ z#@4TA^&z~)ra$-n=bxW`?y2}>pn3M(xmRDSKI*NCEL->I=TA$qoA2J`wa+rw|J!rw zaAlj4y@`nk2TOq7q~4kNzb{`77O}A4;ALYz<+{nR_|t`53l8w|@GN-qRKi%j-*$Um ze*V)Bjz5jm?X9f3m`W-uD@#jTizeornPYS3utvd!dk0!B=P(BL3H8jJ93K^R=up$L zgv7*$PoA*6?%0~{S65V2l$y%Q%{}jx%4E+q;@9R`Rx*h2ux;E>Vs-c2IlgP$+2@=0 z+<w~@C);}F^;bV1p9L{05}y_LH}LWE=bFv-s4@7{dN^_7v7$2;6WbS>PW5tpt<`Zg z$Lw%|y^W1dhUWbS;Yx<Atx=nAzWMWK&&iY~PVu&HF?!)zQ!l;Ta{vAI+ix8zDk~+~ z6qjGtoG-DQWA1XRgFk&{9Xvndl|`sSkW}66I3b&DelGLY?z`3NH}!g6?hU2Ir%qoM z|F_~P>x4(V$@@jNpSR-*`3D5WK63lz<m4)r2%h=4I^Kz;wC=_0<z0K7zE{WFi0v^w l(IBG0>cJR<gPO4TK>VjY_0|hy&hmnCrl+f)%Q~loCIH*))iVG9 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_tablet_ios@3x.png b/Telegram/Resources/icons/settings/devices/device_tablet_ios@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcf36c1d1760eb1e80ecb8344384bdfea5f12d0 GIT binary patch literal 1503 zcmbtUdsNZ~6#n6B*0MCM6mxo3I(z^NALV?Zp$Md@nOphF8KIK-L=yuaM<QQax{}I3 z9GQ=GI6^8)>9memrifrUJq~joJ!%O$1z(uMvvaopw{z}yzkBcZ{dLbhw>TsiZL}A> z7XSbwOdu*u%hX*M>S%MaFJe+lpo}oIAHWqKo!2%f{CAiuK|z3nwl)Mn^h7{!*P;bj z3jok%g8*GEgLY?Zoqtp+Tle33*Jx99Bme;PF&LC@_;t`C^Nk}Z)LayuVPQkN69V~? zmVQ435rRDfvAqv5t$;Ah*=5H+>E{9c`TJ0D`G|@TJ8nl&fyd+;l{wB7dsla=sY5Zu z8s&+L1d5W%TH9V6<%!2X96PQTYXj)~pV%=(p-`(T)uK}A<KwdvI$K&<dk1m?3qPei z7ZLGJq2MCAw~zbHhgS9k)PS*R3Ra{q#voED6FjYPJC|M(X4E9AKY7m9eG5|(Mlzr@ zIJ>QKob2pG7Vv5q91iF4cy9g!Q3_-70?c^~a?ljKpjY*^f7A8>89+`vX*E|60|q6E z=E!VD0P?{J;TLnsrl;K9EzHd!_V)8ir2u*ur9<^@UI`jT&nOg%rKQvZ&2o86^|&}c zdwaiXdz%s+{kvM-CUW%giM5P3{{E>^Ic!IxdBWwYXK|f*Bof{%C0!mVQN{n{T6%ME zU|^j+|M8>Qq2a!*1mRX)m*<wVwY7C)W1~C@fk0eKNm+EvB?_rmt?4mw85y!_Tv(X2 zr>DSs;6<6>>U$9^W>oglvmAHf!kAbb923KG<9zDwb$qzCwpLVBB$Z0lYW3{w6@uHC zg|fkPNDmUd$rV(s%49N)Mx&L7hli)9<Fm4|a&mG`pFW+I#<N>rT54`@Z|C#*t*xZ& zY;@_-q@9wIlD4+C!a^PBfTo+2y<9JKdOk(X%Nr7l?d<F}-wwWhJ-@I3wzjVC#b3Vs zXwv+_)ube0M~4aNWLU<`^MvIVa$;g)oV}~F^NL0j1pyt7J&!7KsDEEvyhk)br_*T% zFSS&`ZNoRFrsBfG&2Z98R$@{T_7$`6!jtnRCJtSXAK!FL*;JVD)p{8nj<xY!)Qj9A z+VYKYw!e6El#xS(N-ZvX`XgqAU0uOM%fr6oLsP<1r;YVn5IQwEdGITLB76^-Lg{QJ zGCQ#Dt9^ZaPIu90w3eR+>Ib6SS65#Z*M+Nwk{s!*4-NK<$cHQzf1(#ChS0rjZE3%Z z%*^0njmS9&os~A7ou2-uO66)iGV!PpdOy!f@<%1FnZ;toTB$%~dtNc541&w_7>zJ+ zJvH^VH^h8wgGtC_z-I)0oY(K(MaawM)Ao7=x$tE}D?(g^jIs7|KqNBA6>>NnpHrI} z(`V1l_|}Y5DHuxsJ(w4+qrH9oZTRQAE2=w>Pj40q`|o9<(R57gS#wV74g1=Dub%Sq za;F^GPL!vIhbcC{_uE4zyHf^(k=Y`mf?9+}NP?86+l2|stn1(0y0nst#N+E|&|F&g z*x6n%S(rp7kMnb{T~nx3t@^|_KRCoB)KV8KD=JJVn3gvIRzU=>BqC9JH!2W~M*I0Q z6Bl&ngHl3E(-WgPW_?qW0}KX(Ld*BNL7ki=a=u_hE|*Ir#>`S|a4^il0c>fRo143` z-x%zndr>7Lh(ngrcpJJR^x#}wx(6em*kICAf_qkvJg_P?40H|{)$MfAk(Ldsknr++ zm-tLC3GSH9pn3G)a#q8K`L!&pW2odeUbvOt5Gga#m~%I^FN|ckJu&g{&^`n=Ewt(J z(tAcj^$Rn$jb=U{C~&pKb^hpcFRsL_5DDo0_dp^Fesx=VE8yY1j_p2IjDIkS>vyT} EFKt+u{r~^~ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_chrome.lottie b/Telegram/Resources/icons/settings/devices/device_web_chrome.lottie new file mode 100644 index 000000000..b97d9dba0 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_web_chrome.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"chrome_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Vector 20","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-38.383,-12.48,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,-100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5,0]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5.252,2.02]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5,0]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":60,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Vector 20","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Vector 20","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[8.383,39.481,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,-100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5,0]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5.879,2.144]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5,0]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":-60,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Vector 20","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Vector 20","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,-27,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5,0]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5.333,2]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[5,0],[-5,0]],"c":false}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Vector 20","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ellipse 18","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[9,9]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":10,"s":[5,5]},{"t":20,"s":[9,9]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1.66,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 18","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[-245]},{"t":30,"s":[-240]}],"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_web_chrome.png b/Telegram/Resources/icons/settings/devices/device_web_chrome.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2445354878639c7ffc9c33b779a0eeaee927c1 GIT binary patch literal 920 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&8i~r;B4q1n1i@ zZ||%?3EzB0t}@-igoFzUDl7-ZA3C_?U0UJ5Ix*uKzkquh*ThB4nIXdLN*sa~<xv7p z4AfmMCUj5nQ&M@E$p7Y^D?8urS{we?MegbRe7~db-^zab`R#Gb<m95s+6v(g>kibf zZAc2){_^F^0F5oXc3GL5ryI?@cmIBU{g%ax7dJOIx3{+kvM&9kuCD(5`}g?x`2Qca z#I4`Hef#puFPCU}J^xv=uWa|($G<t<bl;TiE=etXcInck%a<>!3m<&&^Jishs%uO* z7qe-7O^wJF=}AYN&YVB*eNp`Rl+#ZY`I0XD{JAq|rAWAV!j8LnDZOrw|NgbjZogYs zQSsty)z>Opp1Kd_jHw@|I4#UDo1LAVy+BQf^Vq)o?~}C|4)Xo~dhy2Ebz!UPi)QWM zl|TIc`{7T&enl<5xFSR=C$nXu2TT6$ty{l-`gF*LL$*c3*=%;Iz$Zx_wx3^IKLo8j z!twUan+7gL9d#=MW!@Qf4n7|rJ!<k2?RFKE-uLC@OU=ct!RFT1+_w%LV)8n>&-^8e zz{;;*ONCtIg&$oOKX|fwS<p&@93Au218p8N&!(BBSOJ5C$;^rUaDu@G<--p*9DkhX zyzkuAtD;jcWo;EvGJR8G^<?#x*QIatA3loK5i8>Ob%%4}H>dpjSzA3;olU!a<HnBN zyOYb5W;)3$1T{J|94ai(aLJl+GG#^h%d*`&n%d-S8Z16XWM+9-&7Jnj{o@=z^`)&J z-n_|4OJnm{!&LhA&AWF+Kl$35C&?-v@n;GD`0Sb5OrJ*rj}|PJ2o4SoIKtlcTf5<* z@{Gcj-QC^!`T2JwH=F>vHEOLy#M-dP2`?3%7VX?IYq8?WkgiLrhn*HO?0ot(RVd}x z$BG)0<qLCaDjpmRWp7&Bw>z<>=Fg{3lay-yu9LOueET-{oz{^g!wHKoYP{?{5484K zz{LDU3l3m{SrfeY;*1$H4yem7pUB$Z-|zT)TJML1KW@*8ENgl<+)H@&xiUs)YZUMF oBliCei_B}EGZm5~r)zx>PdK%Yd$n242~e)^boFyt=akR{02)G@IsgCw literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_chrome@2x.png b/Telegram/Resources/icons/settings/devices/device_web_chrome@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..928758262a15589bdfe6aca9eb13ddca5e1c8e88 GIT binary patch literal 1887 zcmaJ?X*k>I7XL?W(W<tzMNzdh)*3VRHdKgEVQ|Ho#5&YYRqbkN8~fJUX;DlG6&hQJ z(J}~;RL9bxO)a&S2^9pzjW73po98^g_jk^F&T~GT<vA%<mIy%saRC4T1kFs1Z8&Uu z0)8&e`xK1J=MX5=1_1+J(<D|nhVgF>W?mK+fD%XN2S7L<fcqqb191)j;8ZyXIK^Sm z$zM6w|Gc*4r~a>>1h(BBxeWlk6f<K(+X&FQld~zxLWD*nR@z+qPv-?i?751YkrkJf z6SI-XzAl4g5v4+*6gN3ueQr2*C^1o5>Bv;Ux{qj;P0U?>OZ}i>KKx@?<3s(C5ad#? z{=r&F^wo85TCdwkanMA-&U42%Ui1=3oDA2$%R>Zrsf1TeXJ@pn?O495b7E4`;o;#Z zy4C-0=9fQ?%Z|vATZ@}4=4>xcR#x`xn;|E7U|^u4qN0%0E%xR-9~yONVQD!rHYNvw zsA0Q4It{Djb$e2Bg&~$P`z*6@H^-j$@2#*{hKpEYUteE1lR=59PSe%ViP)GiG>1@- zvr<x0+Chx5g$gD1{`R+Xel>Y^gWtEdq7V05>VDw`PLdMbk}hhM$c9e023$3W)0ipj zzjjqW<|8Q#uiILzEU%!z@BGX}`%Et(hL@YW&~Fb}-GJ_BZfv9z%t^dEJ3ISqHg(Kh zcwj!D@2GwlR}^1TTwGd;G^LkXfI+#$h2a9`r;fiweCl8mjV{L=b7H~{Rh0w`qH^+= z%jhnD#H~oa+wk!4Fs<}jE&&S(IcDeNSffyI3rDW$w=I5?;^N|ZdV05ImoNm^1Qna2 zlyk22Kle{zRk}t-MrgE$jIzD0C4<QCeGFt~W~LN0N~E^CMN1dCKaRJb^|iCR<ddG9 zoE_FpCdZBxYjT&!KH40R!qbKe)G%l?VOOT9w|D?e3Yj-Sqa~P9&og5Wc480r^VCD^ z{(j?4e~sz1YjEPB_4ELCOcIF{^rd_B)#KBbeLv9|-LVH-OTUUQOIN;nm3)2gQ%7i3 z*!omUa1x1d-00AhxIIT6q|rD%sKLtg7+(g?hzScDVTQQRW>!sw^ldHCDJu2QgoK2u zgRhjQV=duKY`+WX{t7vIH(AXb07QEW3JQkZ<*0}(sT?fdd52XwlbH_r&k=-!g9A`c z9j&m?21PHr-ShFs{Nt#7R;_VbfN+UC)X_7(#W*U8)F64V%Y-z5!C*i_?|yQ~hUxU& zdI;e4(rTv}H@k-ux_X_QDLpha6tSKD&+6(s7<^S(Ny$Ny_qdta(ZJmgDinP5s<l-s z`lmVk^~;{Qy2j?_r&kuiBG3IVO6UZ~58SO#e?-xZ`_Zl>6_nawqgrZ!$6r#&daIrp zbOGbwu)MGUw=i+Fe;RCCqEXe|jlJkk7adRFEo-y4g*|bxv5^@iMQ!_n5iMp$gy6bF zGMS80Zw8WRy=IOft~E6^fm1{Zwh^merya4DhY-WA3+iPFS3{Es)@9J>d?x%tzjn)e z4bRa^cpV~~$KzR;*nO$r?Rh$8XJ?5N(33S3NM#ys<RGAuc5RVUij)W3A;(9t3vkiN zUkf$7hLEj#=+6quFtryhEB6>P)cG5Rj)Fh`@EFKcKVx$^H|PG_tuN85V~ToTjS+|~ zOf&JNw>|`d2!r(x3`{mS4^TC;8Sc^(F{;`zdwYA$hE1s{9_e*kz(yyLktB#jj3Cl3 zp%0uEeus9fEm!(V_&Uphwz3c=TH`#zm^IVh4L3Kpx_6)+p7WG&iI9~K@~Zt6Pnx24 z0~spy4+pW<Tx{=sJ}!$pi((h1ucR6{eKl`IVRIg{7Dq~2METAzrm0jUrlzX8+D9bw zUC|9nO@g1FpJp+?=<7gDuQ&%uLdo5nbje8#Z<au6dit2VOqOA!tfC_LMfHp>qJzsG zszZEPHGsdNs=9zvct711w8W=Z{Nxvbe#P+7j6d^~?FawKxT0-|L@Ee*lG}p5)L6eU za)ls1+od(^{L~)3xxFp_4UfmKUyIalF4qmgq`@?U^0B1y^7vbm4sD64Y<7gW=5QZp z<+t~#8&D|pJ}yZ<zL8&<|5ofiOXVgNA08Gq&ThYhPR+=W*F|JX<gkXcm<;mp5=<jT z3;aoY_3&(MQ&W!04vRJReRQhqX1G_JuKAr2xnPf!Fy{v(|Dtve0YO3e?Irq)wm(gE zBBoHMugC>I;P;<*nm8UBy3;HKd0Jbmn1YYl;tT+rkhKw7e0vYR7q`gSzL;4-oB;y` zj{P>Fihe}%!3AXWj?_aN?}dhfv+h0abM~E+{VI9$!MX2DSr`l!d^DfcR905T(&P<{ zAa!<9oK0q)MJ-arM7SDuCfJ8vH--sTvqsQTo?!mYh)tPsNRBk7Js4!qQkb0ET^(2M qQlmeI{w0r&hjSbMs~qi~XAAWZGa7RZ@8zBxH#4}U@oSiC;(q~ENPav3 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_chrome@3x.png b/Telegram/Resources/icons/settings/devices/device_web_chrome@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..8776b1c28fbfd41dbef05a52e64d41878d2542fa GIT binary patch literal 2805 zcmbtW`8yQa8=o<whC#+MB4i8_5mLDJF}AodWtc3<7A{J;n8-F}vQ&0vhHRrUjXgvu zgUDLRl6~L0vW)CY-07F^_Yb%~yyx?tbKdtk@AI5@`NUaYG3DhJ;|2f#yyj-;t85Ja zEf5a&>Sw4n!3JQ!RZ|q8^t1Rs><!ujXMXF?KLHAC9RdKxcmu${P1qvF761U02LyoF z2>gAP$ML`0@I27}_1}h486qYC0JpO_+Rz3MTz0af3!C$gc?R`gbo)%BfMVlm1T9k) z7jy(#A8d$03xT*z4)}W$wG>@H%by&0s!~AtkQp!-T{%+KALbdd_c@EYU%ZVN)5Z2< zJ@<m>49TON=%bTBlStT2@P7a)4}P;p%okKO4~A`t{pMnSc7#Q`Lb}GCac218ubs7- z+@qr${fO|XCK5B^aO{1k#$iy4-3J0EbZdDec=zYrmmFPCbNykCUfA29<>8gFx`no6 zWIg<&b>*#3nZVlf&h<M42aAKfO`mt~zLhw%c6Xh8NsRO8&NCz{JM|DK?lUj175)*p zD1`KxDY#r@Tk<(un=DhGvs7r6yV9L+^q@Eampa|@=*~p_m|!)4%eL&s6OxQ;C33ai zq2~4=6$~lLr<*292L96{dvCn}Bjxbg_Yqu44Xu#f20w>OlC{Y2T1sTt`F3ruPUK#R z+!J{8Mmcz86f?yi#H5TG;V_?^0pG@5TjEJiP&>aG+eT{cWX%Zh&6POR#KV=u^u(q% zV?P`XReF9f?7ef|-@tv4di=*?ec1kiw1C_%cC;7f8IMx`qG%3pR{KvivE-t<Lz>*V z6XPYMUG4?0jB2S{HvRD0`2N*};59HX)0zksQ5Kh!pV~;%4c$7WaeELUAUE)k_f!*5 zrtbzTfRDR*b%GhOzojJa{pzVzt{$npH&N<xwda={xRO2Y9qafB$(xBpHx~7RXO7N& z0={ja%o)ZLRWpRjghHFc4-bY&Qk;_g3~Ukm1JD@a-pwqX4xP>zJ_$mNqJM0ht7u_n zM#jV1<9zXy={W5A!k2C02%<J%fzNT5m|Y|ZX+SJBXap_cQ1*ESKtboVp63=9mbg-- z1bl@}1CH;pQ=`Gdgm~h#-e}mQ)l<#n@}$LF{Ub9sZfvg))vx!(rvwRYFn?!;(vJxy zlTM37Y9ElXMYidujPG|*RC^1pM6iXCZ|d*Pd>lkp`wj`5MX=MzXup$ues!!a=6NXF z(=<W6@pX)UzWvLqvOoHWJ_Ds;2RoQ_(TR+}AK%)e#bW!)oO9_=u!k7t#+!Sx`RHn= zx*)SknEEgW#J~jKnRd27_L8q<g|6N$Lh$N1Y=j-sLlSwCkbk*N*}0x4h|1IZ>*JGC zTvQ9;W_J=r-8%*PejmBCmYqv3zWSh3H)NxGxVOw%pZ0f4ys%4_<>+fa2kJ4w&fWC| z4~|qdT!Z}VWID2<m58yp;6G`AP*@>d^!euHR~vB@Uh4QJcI(2eF8=1w?UnRgSC;+c ziW#UtXDeX7>;C2<_G@xk)BL9_j}lj>@U>}U>5&(fRFsYB0eBb4r#sZNpXu2r*{Hlt zGNN8~>mhU*1v1iPTc10YI%<i8>OI)8e(igm_G7X!x{-tIGT%jQDPUJ(U7P9yw5^kG z6ptvEsG1+ibxldCJ?;CSj2{DKXNKJd@IPnUuUFoR3#!7ec*f%Vgi-F>Ml{{@47=On zYVNV!O7)zUXJ7RUs|nc00V!m5?J9_6se98rs=SEu2zU$^xxL$16E1_0p`#k~^y%0F z6JD!1_Up(KCf&@iefY~3f@(vbiq;~yX6&`!2>Sdy=TdW59Pioam~okAP~tqhl$~s< z))jm>T~m@rlGkPIcsqp^Y*d~-PFd<mS5#&?u0h6w$OYV^Ac9&u2fE#V(%KK9qFeeB ztiR|4NJq%q(7TCTg0pbUM{hVaik9gPq|MH#PAkDV=8y!=P_uLXmqhs#(F6s%(qabZ zGalKKDec{Esxe0GNtmSPrrioVm^<Nn0Paf9FShv+M~A<P?8^Fr>fo^igGQ*j@zA@Q zt#iuy`0rDi0jpQTxf6M{HZ`rLj%T!|oK5u>!-%anBKBI2S*ArRpm}t=8q6<`f7O22 zcqExdYCdoC%nSx?kKMxgzo`VD(0#4E;*?TISuuTl(ohVvfrDY#ofR2IwQ0Z9PL$;k z+u>?nbjL-y%&I3{NdPxgxVH-!mAI?*?<VWKYE0bA(zvsSh3+%kQ_pld4icqI<ZA}g z5okWqrsO;)dmWN9Gkh#nE`$IC9c!CuDSxc<YPi}*a9P7|*uAY(7NiXutoE@}dhUtr zc@{_4w1q`)RiQ2XCmVpSC-~I99a^X`7Nq{pUmmVh#q8wBi-!p-mK3Vma}4#*Q7~`+ z_J(dWN*{+vV7}!GfCXr+eb%6qggoQxBe6QOI`@Aj-B7`YebSM#ZPiXOEME0*6sKK% zQ1m`zvq##ehthV%v7Cutqt$`=W!}c<hacdBZ;%UL3c*|3E^g1#FP-Vo{*X}V-TOk5 z;G!25+Tel)V%4W%alqJ$>ohdiMJ?lHyAeXH9IX{)!!1<2Al@2&F{E_(@$9jSOhZ|t z&Uj(Ps`5;Axjo^(wBwDDAM@f|l-1jway18zmb(~1?Vejdp5<)eaI}&wzUD<7GUK<~ z*2($R0cNFTYu-gcHXICW>F1O@I>Pog)_fQllA_{xAmh;|Fr+xip8D{KO5-!~;MbeY zoOlg!4LnO0=DsAglmHP?3o572f5|-^BQYy@CHoI-pt);x#11PT(V-wJoqg|y^%5)4 z4)SuGVIEa=b1!3+9SioAyE(0^%DXq^pGVETaV#Oay{4KH>uNB1u9IwaD>_TEw4;0i zJ`YT-(}w#);pn&hGeu>fa{f7?B)R8@51WoDCmn`tEt&g5UteA+GFV{uj5EE)kzBYa z040t8S(a=x3A(yA(`sHU?Bk}kZ$En5vZJq76=`ypHEAdUUY2yJ@axV4ss;^~y9D(# zkQBe};CWz_pV((661pLMVnRyD+pRFi^0BHN{xYPv!<10K0RM~=1IKfB)d5N~Mhd7; ziXg{6Wodjt3E}uTc&6$g=n?B8Gw{g!&>D$9h(4pWchQ>)SjJ!&m*{CHYb$p&-h@6q ze#oBlTnXVtv7vN3m*C~$IXk}Z6%fwpLW?KCpqQ>M>V>%(x^tVqc`{|V!p)S4GSFua zWS-hm0k6Kl3QV5(i;Qrbe|$}vm$fTQnmzrXC8}-r34^j%F}FQu)q00h0|_x<oBiD6 zh5;~SFX{Cf`t(~t<+xL>F(+Vx*5#;pe10MSXwKY``J@P73iy!Oc!5>&0)pyb^9Wto zaUiPpedzWTA?7dRtch|CGT=65YX4{4(*&exf=qc|iNiqH8xzM<H3P5(<l!;tn4%*@ z<T*UpfW<dkmqAhN!}U*nh^aP`%omnQ=)UZ$k+5-1;%?|Ji-drR8`ZJXgUw=^BDAD^ zV!vce3&pz$&4+Xu@7RmnI6<?}l-3F$TXuQ;SKjuU_7%f+M68P!mfZ5cz5h))&5f_1 JOHnro{{lu_7rFod literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_edge.lottie b/Telegram/Resources/icons/settings/devices/device_web_edge.lottie new file mode 100644 index 000000000..487209ad5 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_web_edge.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"edge_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Union","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.003,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-0.082,0.078],[-0.014,0.019],[0,0.42],[0.001,0.035],[0,0],[0,0],[0,0.009],[0.002,0.03],[0.293,0.391],[0.348,0.176],[0.39,0.003],[0.247,-0.117],[0,0],[0,0],[0.009,-0.004],[0.226,-0.375],[0.005,-0.438],[-3.697,0],[-0.897,0.336],[-0.267,0.139],[-0.069,-0.011],[-0.048,-0.051],[-0.007,-0.069],[0.037,-0.059],[2.152,-0.744],[0,0],[0.253,-0.054],[-0.228,0.072],[2.252,0.966],[1.04,2.219],[-0.014,1.51],[-0.03,0.216],[0.003,-0.209],[-1.868,1.842],[-2.628,0],[-1.655,-3.238],[-0.012,-0.024],[-0.003,-0.006],[0.019,-1.66],[0.417,-0.725],[0.722,-0.422],[0.832,-0.002],[0.001,0],[1.105,0.768],[0,0.204]],"o":[[0.024,-0.023],[0.395,-0.514],[0,-0.029],[0,0],[0,0],[0,-0.009],[-0.001,-0.027],[-0.029,-0.486],[-0.233,-0.313],[-0.348,-0.176],[-0.533,-0.01],[0,0],[0,0],[-0.009,0.004],[-0.384,0.209],[-0.226,0.375],[0,3.263],[0.958,0.002],[0.282,-0.106],[0.061,-0.034],[0.069,0.011],[0.048,0.051],[0.007,0.069],[-1.215,1.926],[0,0],[-0.193,0.061],[0.234,-0.044],[-2.325,0.775],[-2.252,-0.966],[-0.636,-1.37],[0,-0.213],[-0.033,0.203],[0.041,-2.622],[1.871,-1.845],[3.886,0],[0.011,0.022],[0.003,0.006],[0.283,0.552],[-0.002,0.836],[-0.417,0.725],[-0.716,0.425],[0,0],[-0.073,0.003],[-0.236,-0.165],[0,-0.191]],"v":[[1.84,1.691],[1.9,1.628],[2.509,0.073],[2.508,-0.023],[2.508,-0.022],[2.508,-0.021],[2.507,-0.048],[2.504,-0.134],[2.011,-1.479],[1.127,-2.223],[0.004,-2.495],[-1.181,-2.196],[-1.181,-2.196],[-1.183,-2.195],[-1.21,-2.182],[-2.142,-1.29],[-2.493,-0.049],[4.423,5.757],[7.228,5.252],[8.051,4.885],[8.251,4.849],[8.43,4.943],[8.514,5.128],[8.467,5.325],[3.264,9.45],[3.162,9.484],[2.478,9.66],[3.171,9.487],[-3.935,9.19],[-9.045,4.245],[-9.991,-0.13],[-9.945,-0.774],[-10,-0.156],[-7.022,-7.122],[-0.001,-10.001],[9.114,-4.9],[9.149,-4.832],[9.157,-4.815],[10,-1.47],[9.361,0.912],[7.622,2.661],[5.258,3.314],[5.256,3.314],[2.011,2.635],[1.642,2.071]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":40,"s":[{"i":[[-0.082,0.078],[-0.014,0.019],[0,0.42],[0.001,0.035],[0,0],[0,0],[0,0.009],[0.002,0.03],[0.293,0.391],[0.348,0.176],[0.39,0.003],[0.247,-0.117],[0,0],[0,0],[0.009,-0.004],[0.226,-0.375],[0.005,-0.438],[-3.279,0.493],[-0.897,0.336],[-0.267,0.139],[-0.069,-0.011],[-0.048,-0.051],[-0.007,-0.069],[0.037,-0.059],[2.152,-0.744],[0,0],[0.253,-0.054],[-0.228,0.072],[2.252,0.966],[1.04,2.219],[-0.014,1.51],[-0.03,0.216],[0.003,-0.209],[-1.868,1.842],[-2.628,0],[-1.655,-3.238],[-0.012,-0.024],[-0.003,-0.006],[0.019,-1.66],[0.417,-0.725],[0.722,-0.422],[0.832,-0.002],[0.001,0],[1.105,0.768],[0,0.204]],"o":[[0.024,-0.023],[0.395,-0.514],[0,-0.029],[0,0],[0,0],[0,-0.009],[-0.001,-0.027],[-0.029,-0.486],[-0.233,-0.313],[-0.348,-0.176],[-0.533,-0.01],[0,0],[0,0],[-0.009,0.004],[-0.384,0.209],[-0.226,0.375],[0,3.263],[1.152,-0.173],[0.282,-0.106],[0.061,-0.034],[0.069,0.011],[0.048,0.051],[0.007,0.069],[-1.215,1.926],[0,0],[-0.193,0.061],[0.234,-0.044],[-2.325,0.775],[-2.252,-0.966],[-0.636,-1.37],[0,-0.213],[-0.033,0.203],[0.041,-2.622],[1.871,-1.845],[3.886,0],[0.011,0.022],[0.003,0.006],[0.283,0.552],[-0.002,0.836],[-0.417,0.725],[-0.716,0.425],[0,0],[-0.073,0.003],[-0.236,-0.165],[0,-0.191]],"v":[[1.319,2.836],[1.379,2.774],[2.509,0.073],[2.508,-0.023],[2.508,-0.022],[2.508,-0.021],[2.507,-0.048],[2.504,-0.134],[2.011,-1.479],[1.127,-2.223],[0.004,-2.495],[-1.181,-2.196],[-1.181,-2.196],[-1.183,-2.195],[-1.21,-2.182],[-2.142,-1.29],[-2.493,-0.049],[4.423,5.757],[6.189,5.293],[7.114,4.78],[7.313,4.745],[7.493,4.839],[7.577,5.024],[7.529,5.221],[3.264,9.45],[3.162,9.484],[2.478,9.66],[3.171,9.487],[-3.935,9.19],[-9.045,4.245],[-9.991,-0.13],[-9.945,-0.774],[-10,-0.156],[-7.022,-7.122],[-0.001,-10.001],[9.114,-4.9],[9.149,-4.832],[9.157,-4.815],[10,-1.47],[8.84,2.058],[7.102,3.807],[4.737,4.459],[4.735,4.459],[1.49,3.781],[1.121,3.216]],"c":true}]},{"t":50,"s":[{"i":[[-0.082,0.078],[-0.014,0.019],[0,0.42],[0.001,0.035],[0,0],[0,0],[0,0.009],[0.002,0.03],[0.293,0.391],[0.348,0.176],[0.39,0.003],[0.247,-0.117],[0,0],[0,0],[0.009,-0.004],[0.226,-0.375],[0.005,-0.438],[-3.697,0],[-0.897,0.336],[-0.267,0.139],[-0.069,-0.011],[-0.048,-0.051],[-0.007,-0.069],[0.037,-0.059],[2.152,-0.744],[0,0],[0.253,-0.054],[-0.228,0.072],[2.252,0.966],[1.04,2.219],[-0.014,1.51],[-0.03,0.216],[0.003,-0.209],[-1.868,1.842],[-2.628,0],[-1.655,-3.238],[-0.012,-0.024],[-0.003,-0.006],[0.019,-1.66],[0.417,-0.725],[0.722,-0.422],[0.832,-0.002],[0.001,0],[1.105,0.768],[0,0.204]],"o":[[0.024,-0.023],[0.395,-0.514],[0,-0.029],[0,0],[0,0],[0,-0.009],[-0.001,-0.027],[-0.029,-0.486],[-0.233,-0.313],[-0.348,-0.176],[-0.533,-0.01],[0,0],[0,0],[-0.009,0.004],[-0.384,0.209],[-0.226,0.375],[0,3.263],[0.958,0.002],[0.282,-0.106],[0.061,-0.034],[0.069,0.011],[0.048,0.051],[0.007,0.069],[-1.215,1.926],[0,0],[-0.193,0.061],[0.234,-0.044],[-2.325,0.775],[-2.252,-0.966],[-0.636,-1.37],[0,-0.213],[-0.033,0.203],[0.041,-2.622],[1.871,-1.845],[3.886,0],[0.011,0.022],[0.003,0.006],[0.283,0.552],[-0.002,0.836],[-0.417,0.725],[-0.716,0.425],[0,0],[-0.073,0.003],[-0.236,-0.165],[0,-0.191]],"v":[[1.84,1.691],[1.9,1.628],[2.509,0.073],[2.508,-0.023],[2.508,-0.022],[2.508,-0.021],[2.507,-0.048],[2.504,-0.134],[2.011,-1.479],[1.127,-2.223],[0.004,-2.495],[-1.181,-2.196],[-1.181,-2.196],[-1.183,-2.195],[-1.21,-2.182],[-2.142,-1.29],[-2.493,-0.049],[4.423,5.757],[7.228,5.252],[8.051,4.885],[8.251,4.849],[8.43,4.943],[8.514,5.128],[8.467,5.325],[3.264,9.45],[3.162,9.484],[2.478,9.66],[3.171,9.487],[-3.935,9.19],[-9.045,4.245],[-9.991,-0.13],[-9.945,-0.774],[-10,-0.156],[-7.022,-7.122],[-0.001,-10.001],[9.114,-4.9],[9.149,-4.832],[9.157,-4.815],[10,-1.47],[9.361,0.912],[7.622,2.661],[5.258,3.314],[5.256,3.314],[2.011,2.635],[1.642,2.071]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0.005]],"o":[[0,-0.005],[0,0]],"v":[[-10,-0.142],[-10,-0.156]],"c":false},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Union","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[1090]},{"t":50,"s":[1080]}],"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[11.667,11.667,100]},{"t":40,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":120,"st":-60,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_web_edge.png b/Telegram/Resources/icons/settings/devices/device_web_edge.png new file mode 100644 index 0000000000000000000000000000000000000000..4f3e0d750e4579749077372b2967f1cebbfdea4a GIT binary patch literal 765 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{Hv<Dxx~Gd{NCfBG zFyGUefg*K=Ass4lUM`Xk*k>3Bas~cjbWUBOk*fE<;YqTRv#_&fs)q;{b9s`(6YHP5 z%1gh#+OvN1`J2Zdl(DD#ui5-<^Ze&?woco7?Ku1H8yp)16O=#rD<4Q)wJd0*ja>if zr<Y!q6z%*|w)^j|-M{|UtqhUkYxm(S{5XI9{Fy$_fHH3D!&ZxOuoOssEX><pz4_*X z(*18mx?Sz&_xt-b_qZs1|NVD1)7`@f1_2r(CVMPyPssEzm@9L#Zg1T4qMa5(9y~P% z^R_ELJXWkK#HnI>&dqH0QDG%_$GKTsqhu6?c&7aNTBWLf?Ax*+O`%u%;-PspcJlql ze>?5_nZ5n?eVfSDR|DiZ>sd~$4$%r-ef4C@q(x@iZ@-=WY4Jr4RsKmXmoiMcoaFe} zl_oXT+Q<bik?L*pP!a16JdwBE+vVSel|}rY>@S>m-@!QFOhSflQ+bG%C{XF$*Ixtf zr)-VtblDiuBVswzr^QK8@Z^DYVXK!cd7vZ0<)ZYWa{Y;q6*&eH9R_J-8X{GywG|7l z=!%`5d@jZ4>E}v~Pz{q4-n;Mi9e>Q!_<*zGctg{ojP>0|59OHcKiplfHvRNMCWe2P zUrzB`?y4lHxzI&v;ohwiM1LntIhi65^vXbQx_8BCx5b8Y{l5RUz2iE8yZvzD=9@ik zk8AAIYb|4zUw^%H#}$pF$KQV!nyx5(STN(_4#orvr?V$>#OrTFtqnVvFhSCqPs)uy z<G+aRj~ctfQ`jHt^WA244z617V!+~{TxK=5YH!@ikftY9d*78<olV;uqjx@S^L6Dl k?Y#`rjAl&#{aOAoJUIRRqsgIQM^Mu9boFyt=akR{0MghxR{#J2 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_edge@2x.png b/Telegram/Resources/icons/settings/devices/device_web_edge@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..451475472e0b96275114fc4fd15d3e94c27b27e5 GIT binary patch literal 1503 zcmZWpdo<Gv9RJxomb_}>OikV`REoMu-t(A^X*3~(WAn(#BkH=!V_uC)W*#@%%)PZN zZ;|R6h9yKe-tWg^qTO6<bam&S`#AS|&gc92zW(^0@A;n3Jy#b8d091C0087sj!2xC z-S+?ii9KasdY+ghqHzv3K*fh6%i;tXbOl9lb_VptF$9oE4*?|iM#NAP0|3Bu2>>i+ ziM<*f^mo^t4*n<J8*B*fu>t@o6bfnWep_PIi;Ff#DV@{c0gM{LMMyF|03`KM%I;LM zgq?C0Gh?PDQELv>XW1ed&`g=tk25-kyn=!7txpGh_;JhDGvO=2w-<wb_+_7YI`=W3 zU&(4(TYg@q4u#&0ls*qGlK21+6OEf;Gjb9CO}{BfV-mVC1_ms)<w-2Im%Yn2G&DSU z>QrS#g$owz;o<RJ^h!ll)z8=WZnu%VOjJ~qzM<g}b@iq<Z+1MUVj?3y4h&H8@(3Xz z&r5=SwizE885|mVLZfv>dm>k6X6R3!cC@ruyP3gAfv5ASRJgi2`TQ?eQ_>j>Mr(I> zRyEbDc{cLN<Hv4mrO1aOk*K-Z>VzC24F-cXve~C*Wy04v9NW0K3&!4~pvA>Sa|?_9 z*Ltk!ot>XzV$OQk!stOkL7iu4sW=}WI5^YO8i_<+bFLAfRit^A+S(-Q?`086byZ^{ zL5;g5k`*Kp>GEiKQ*C3TsJ}laz7!U!rDP)5sbn%UN-Uj9l$@C~ODD;;2uN$LSGViY zB%@ZM(~2|3J=K{?r9wiOT>SKbC;9pLEbgVO#H7l~N|=i_d~j{;<OT(6x!&2?xw*MX zHVvF?>FP4j(@RsJPKk1Jm0xQXZjm!H10SG3ZTMRW3D^eL!Y#GKhqXt`*!QD&P$;y# zynKU`<K^q;=k4ts7#KJ?F;P@pTpc_<K0ZA)<#;C}d}?Z{Eq3UBPfyQHXhxA@A+4`S zAP`U}&<owQwZ@wCw4A0U3t8Jo>ze!fi1^@Ogo+gWg$2fWfkL4u;{#q&%0t4!N)so? z#<V#6uwUbOcjDv!&^)*>IXRhvH$mxV#7>Wn9-yLSj~CyHiJ_*b=wH8n9g;e&+tt-o zAo_C9^t%ThzrfrVgD6h8dsig@<>We|v!dakV~|lF8yic0ubZ0tLA<Mz&Hh6=NAs$x zjNIIUJ|X7k=T}x%TB{cv5ycJ{bY*-TxV^nt2S&6v4#&{eoSs|l>+7=`@}It7WMox+ zVry#)ifP&LJFfS@1d`}=R&2-5DsMB*m(lF%v4)0*L;m6m>))}zxkSIZiO^gIIvd+P zgT&9r)SKu~QwcvPTjF{7GL=Fh`L^PFbR7wi*=4~D3+f{U(auihSo%U8i?y=6d=JQc zz54E*qq}=9A{kdwQ@&xg-PR`Qqh)SvOnPBIeX-OeWqmX|JDX0|P*hYjGc$`wB1@^u ze&$~zMn|u|fu)IPG#a)*D+;=a?!49H|2D@|9~W0d%PH2WQ&3RYotwWEfo%AcaJr_x zJ`LldH*Eim5EjPzicxt(K%@7UsgdJ3+N4m~da&JZBL+kwF`-9i<LH724u>;|jzj)n z5GcZ_v7sTo`i!yh<!xK`c!K;zb8~U~&lna@kwIwV3+rc>mLRF{++cyT?2y#S_(Ji% ziNmwUb_WNQ`FWvaztGR$28M@+RqJtBGdl;t+}heM7sT1MjpTF5jRmVh;cd<~%PcN5 zRKJ^1U~lK6q_)W8@gIcCZ5g2ldT~mk<bXhd`-JqWg=1@@#<`2XU%Kx=o{5i*#eWRQ zb61DMsV_jV{a{4<Ayrjl)7RV-@cS!AC^ux1zi)5H@rB=o!dKPRAKTmQb~EgsEiWx) zlyUm{vb%d<R?)QI9Y-L9Np<PgDaR-`bU-K+>Rtb(j&E-@tPcr!JZW|TQJ8ft(%(O+ o!o+YuuA<}r()BMT6C8#K6(_!;({+9*RoxR;l${H*!sc4?UlP=~g8%>k literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_edge@3x.png b/Telegram/Resources/icons/settings/devices/device_web_edge@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..cd347cea8e3fad2a3d845ce19891ac94c24af7d0 GIT binary patch literal 2215 zcmbVOXHXMt5)J|ydZhP`D4~TeBm#aE0YM0a76?_E6lnoNF;bM!1*F$dM5+Wu5HNrs zMa2dx-5`RYgCLj?xHxlj_xtY0v(N6nJ3F&Gv%4?O+RBuJRhSh30C1RHF@n=M{x5*1 z=rYJqZGlb<;c(N-fR=GlGTkt`?r7#?X$erE>p%d*J%0ex-w-;4=>Py2D;WTcbY}SL zt336e*Z4}t|LT7OCG!PL=qb*c85!C|Gi;POT|v0=4!2)@%lZ&5QrUk5`XIV1<-c2X zF^{SGDlWn2s%6y~iJAfsIAyhY<g&4_f1k9JYzB&8({8OXQJS`Qqvj8_TCzZIGj4o- zWj%%%DZVrt_XTyMb>UJ}Mcii;RVP?f%m@nvF-RLQr2x)Kp88)6><nldPOSYtoaNSe zlY}=;I~KaQ{H{U*1X7J$ow#6IcSNH`MMobl+-H>U?d{FT$WSrKJlI-j+$zQ^v9qzg z9v$tv^=tVamjt%(;3t{f74~E7b2IY8wNy;Kb9-!TETO%9YrdDd_C*CXi~e!@=+MU2 zwmoWVzOT<<vJ_DZ6Et9DW9#nj-r$~aJKXs`>@}SEwpi1G?;gT$IEjUaQ&gui$epy2 zv$a61(2G)5Q-d=PTL!;O<pbXgS(2-_6&JI1ae;;|8s(O0zlPr3=aIMinN<If^147p zKm`?;Ppz98x^_I8E05`8c|@fiY*m|Pc^(G{0~0wO*3`HQD<_qK<O&BURH_JjKT~*# zcE~GhFjJ9%90vyni9|a0P4&&v=3Mv5(H|L5kpZHttc;CcQI$Qp>aJGMbW$5aQjyf) zj6^0TCUPWuV%rdn_7`-<zoJobaj-hnX#iw|$rEKh=MaQL=G)oX2@V=}2)Vf}3};Co zDRsverK1RKf>kGb<WH;Z9RX5{@6~<BH4-wRA{P0IHfVFN<eAm)wH5_S9`{?OW@cs% z57YS!VG>`OoMg)G%Q7-EGh?9Iy~@kQofz5v`}OGErBQmb)x|2tPY2F)SW3&7HMDJ3 zeG1}hAjZ?2xh0$eXh%mB3dPR-A(4aLp@KVR_K!7SusJWhjUIYE`wlAKs_DiWahCT3 z*4S0iV@z7kUPau5mn(M%tw6vIN+)PtVB|Cr;X)obbp#g0RGMbPqyKq}C@d{i>|t1J z#jvM2p76}Sha@{%O+O29i-^$pI97M9qC77zucRbxd$tgp&U28mf@R*iGIl;IZ|nis zL07ul<K^jFWZPC$Z<x13)$Yd3Gj;qDcg*27>52cRQmPOy@7^BSR{3fA0F6c)w*>Df zg@FZW9s)7c-$8zU=hI>Y6Q*ZorZm;fr9*Rvo{y?cPEMvDKTZpsgK7oOj<ginaFb$k z+X#iXf3FKw>!*8mwi3QR&FlX;SYK+>R9*cNPRtJ9Q5PlsS@#_;B}F0*7Xy$;kq{A= zu{6^xnz(Xv7>j!@umGAQ0mBR;hq|C)yk%^DhxSkmNhm_NrL(T?T$6b~AE|zfMm-RV ztL<`uQbHD9_y=dqr3y=Cnq;pBe6mg%Jc&8j^soQflN!BOp?@14oO6KW5D8s=QzWDM z+0n`A&##qh^FG~qQH*=xE8~&myAQB^lH5LfUr&42x{a9b{5(6y|B(QZ3k?nRT1l7J zi`w+gi@1QMPwAk%OX>wFYZPi+$H|Ac8?&!a;I;O-*=u4Q-c(zA{bP-p%jdVe$_ff$ zWbOh$u(%`ndIXCt5_)`-93{)%&BDTxj(z}osf&~G)hO^<fmDY)3!eKJI7E=%Y+U=& z>Z{R^Dv17sq)0B6LPWEc0;NRC^y4T`&>PMOY}nI(OlC!Au#VGMk;L)w;NalQtSsNQ zTKpS&J&}|Uq!=y`2<+bDk4h#G)PmA>-H7q9uq7b;qpP!xgZt}zrH=Kz0+$!DMIU@! zI(*-iiwO&RYNS5MrmrNCQXSbJwTe<wQdeNPZ322=aAQM5Y8&HwfFj8=KcPG&BZJvV z(JRA>9};62UD?>k7Ips&m-xiwB<bEjy?rEv=G8LC#{VwA(D2b_PuB*r7xUB=O<}{m zNGspp-NpP`&WL}H*2G>4mREM(zI{yj)}M&($0_vOBqeO~LAb~&Z+>N)NwPk1g>_zI zWrt&c2R7R1jp^$GMSYhQYg~Gw&)#yt_er($dDKs}`=-JDGQ|UpFvmw-2JfxxJUCP| zHTez%gIYZO<&=~-%uOsyp|{{A;>iIdX>M+Z&{K1};3TCT)dxjIIDyjy>porf`NBJo z9{>0yAlwr{hUc$hyfsiVF6S&PEV#sU2NA|8s(8p(U0ofW6iKGz#lBXMYl2b72O<Ol z0sfwv9>Chd;hxCNc)93?rsn3SJz9uM#PF5OWgCm37yUfYj>Sh;W1j>Ss!6fpr43<0 z#o8hCHH;nG+o?I{#g-`UH?X?6j{&}EFq7C?9UU0~EjJ{jek<>Ae?GxU-Tjf)t!xpx z4^ca<=o<zvBAg%A%V94x50lC{pI+eNxjZ~z+`;Zsio@Z!T@lur1p4T8N|iK)(;Q`S zDJj;V%)N6Sl-R><4_lO7Qa?;zf8nLpL0FXFa(K?~@{7-xcC7TX;wt1+;*(moi?tbj zXe00`;%%ZStZA3f0nTDx>*;|CU4(WXwH;;fY6*e&(Q%=#FMDT`<Q_i@S>Wg18dM+L zU;A>sht#J;Qq#h`N*9Xxz21f1D873S;JxWVN%DI<CbFIZ6jmpoW$)@S;JXh`uIS+L zc+l3+9Z7*PNRE3#(hEaQ9?n4fS7Yp)P>qqVXBO*|?X3R4)B5jo91Xp50)X?EIDy9* R{(dU}X2w=VEtlOB{sk441Q-AS literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_firefox.lottie b/Telegram/Resources/icons/settings/devices/device_web_firefox.lottie new file mode 100644 index 000000000..2db4fbd72 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_web_firefox.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"firefox_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Vector","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.974,-1.629,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[-0.03,-0.196],[0.001,-0.315],[0,-0.044],[0.784,-1.26],[0.037,-0.059],[0.129,-0.308],[0.711,-0.509],[0.55,-0.259],[0,0],[0,0],[0.07,-0.029],[0.18,-0.163],[3.652,0],[0.988,0.296],[-0.036,-0.014],[0.11,0.04],[0.723,0.489],[0.845,1.898],[-0.075,1.505],[0.08,-0.373],[-0.467,1.186],[0.175,-0.384],[-0.528,0.892],[-0.21,0.229],[0,0],[-0.018,0.121],[-0.293,0.65],[0,0],[0,-0.031],[-0.004,0.014],[-0.041,0.083],[-0.067,0.076],[-0.005,0.007],[0.006,-0.047],[0,0.004],[-0.041,0.093],[-0.224,0.154],[0,0],[-0.14,-0.364],[0,0],[-0.016,0],[0,-0.005],[-0.039,-0.068],[-0.052,-0.077],[0,0],[0,0],[-0.157,-0.134],[0,0],[-0.764,-0.289],[-0.118,0.088],[0.005,-0.039],[-0.257,0.109],[-0.001,-0.033],[-0.368,0.071],[0.068,-0.058],[-0.176,0.009],[0.062,-0.026],[0.117,-0.057],[0.035,-0.028],[0.261,-0.684],[0,0],[0,0],[-0.485,-0.077],[-0.041,-0.152],[0,0],[0.006,-0.04],[0.233,-0.198],[0.07,-0.045],[0.28,-0.123],[0.316,-0.196],[0.045,-0.044],[0,-0.005],[-0.011,-0.056],[-0.036,-0.068],[0.009,-0.115],[0.083,-0.192],[0.101,0.051],[0,0],[-0.046,-0.118],[0,0],[0.09,0.05],[0.113,0.049],[0.012,-0.004],[0,0],[0.129,0.064],[0.093,-0.166],[-0.179,-0.324],[-0.259,-0.177],[-0.015,-0.012],[-0.218,-0.109],[-0.068,-0.039],[-0.064,-0.034],[-0.763,0.111],[-0.451,-0.592],[0.366,0.116],[0,0],[0.762,-0.423],[0.818,0.173],[0.199,0.069],[0,0],[0,0],[-0.562,-0.34],[-0.416,-0.078],[-1.148,0.914],[-0.001,1.467],[0.272,0.642],[-0.488,-0.931],[1.067,0.88],[0.381,1.005],[0,0],[-1.363,-1.273],[-0.526,-0.531],[-0.141,0.387],[-0.334,-0.559],[-0.386,-0.63],[-0.079,-0.119],[-0.165,-0.347],[-0.16,-0.563],[-0.039,-0.466]],"o":[[0,0],[0.055,0.31],[0,0.04],[-0.062,1.483],[-0.04,0.067],[0.117,0.313],[-0.218,0.847],[-0.474,0.382],[0,0],[0,0],[-0.072,0.033],[-0.104,0.22],[-0.111,0.139],[-1.032,-0.001],[0.036,0.016],[-0.114,-0.038],[-0.834,-0.257],[-1.778,-1.076],[-0.63,-1.369],[-0.123,0.361],[0,-1.275],[-0.241,0.346],[0.247,-1.007],[0.158,-0.267],[0,0],[-0.004,-0.122],[0.065,-0.71],[0,0],[0.006,-0.012],[0,0.031],[0.026,-0.089],[0.045,-0.091],[0.005,-0.007],[0.005,-0.007],[0,0.017],[0.027,-0.051],[0.09,-0.257],[0,0],[-0.013,0.39],[0,0],[0.014,0.02],[0.003,0.001],[0.04,0.082],[0.055,0.097],[0,0],[0,0],[0.115,0.18],[0,0],[0.797,-0.161],[0.095,-0.112],[0,0.039],[0.171,-0.221],[-0.014,0.03],[0.323,-0.19],[0.048,0],[0.162,-0.07],[0.288,-0.03],[-0.122,0.044],[-0.035,0.029],[-0.613,0.401],[0,0],[0,0],[0.108,0.479],[1.373,0.129],[0,0],[-0.002,0.04],[-0.056,0.301],[-0.062,0.056],[-0.047,0.027],[-0.344,0.141],[-0.053,0.035],[-0.016,0.017],[0.025,0.051],[-0.063,-0.071],[0.04,0.108],[0.007,0.209],[-0.091,-0.068],[0,0],[0.106,0.069],[0.034,0.095],[-0.059,-0.084],[-0.132,-0.078],[-0.012,0.007],[0.032,0.057],[0,0],[-0.171,0.083],[-0.151,0.338],[0.174,0.261],[0.016,0.012],[0.2,0.14],[0.066,0.044],[0.061,0.04],[1,0.525],[0.733,-0.129],[0.261,0.371],[0,0],[-0.374,-0.124],[-0.755,0.359],[-0.207,-0.04],[0,0],[0,0],[0.383,0.533],[0.377,0.192],[1.431,0.327],[1.148,-0.914],[0.004,-0.698],[0.904,0.537],[-0.606,-1.77],[-0.858,-0.646],[-1.103,-2.96],[0,0],[0.276,0.257],[0.074,-0.405],[0.11,0.642],[0.512,0.728],[0.083,0.112],[0.221,0.314],[0.262,0.523],[0.128,0.45],[0.127,-0.167]],"v":[[10.333,-1.238],[10.393,-0.927],[10.475,0.011],[10.475,0.135],[9.184,4.32],[9.068,4.509],[9.048,5.476],[7.611,7.575],[6.069,8.54],[5.987,8.579],[5.942,8.599],[5.729,8.692],[5.298,9.271],[0.329,10.801],[-2.718,10.353],[-2.614,10.398],[-2.948,10.281],[-5.296,9.156],[-9.325,4.587],[-10.17,0.211],[-10.475,1.314],[-9.767,-2.41],[-10.394,-1.312],[-9.224,-4.176],[-8.671,-4.921],[-8.671,-4.932],[-8.65,-5.296],[-8.109,-7.351],[-8.094,-7.38],[-8.094,-7.305],[-8.094,-7.261],[-7.992,-7.52],[-7.822,-7.772],[-7.809,-7.791],[-7.831,-7.656],[-7.831,-7.632],[-7.736,-7.827],[-7.253,-8.459],[-7.242,-8.464],[-7.05,-7.322],[-7.05,-7.318],[-7.009,-7.374],[-7,-7.366],[-6.881,-7.137],[-6.721,-6.875],[-6.708,-6.86],[-6.697,-6.863],[-6.282,-6.389],[-6.275,-6.383],[-3.888,-6.184],[-3.568,-6.485],[-3.575,-6.367],[-2.922,-6.871],[-2.942,-6.776],[-1.898,-7.171],[-2.121,-7.035],[-1.609,-7.156],[-1.058,-7.036],[-1.416,-6.884],[-1.311,-6.863],[-2.655,-5.193],[-2.655,-5.182],[-2.655,-5.188],[-1.674,-4.268],[0.003,-3.872],[0.003,-3.801],[-0.009,-3.681],[-0.456,-2.909],[-0.654,-2.756],[-1.214,-2.513],[-2.206,-2.008],[-2.352,-1.889],[-2.404,-1.82],[-2.351,-1.658],[-2.434,-1.635],[-2.387,-1.296],[-2.502,-0.687],[-2.791,-0.866],[-2.802,-0.866],[-2.567,-0.579],[-2.579,-0.462],[-2.806,-0.665],[-3.301,-0.919],[-3.334,-0.912],[-3.244,-0.746],[-3.453,-0.858],[-3.859,-0.476],[-3.814,0.576],[-3.159,1.239],[-3.294,1.203],[-2.666,1.578],[-2.815,1.564],[-2.628,1.674],[-0.165,1.546],[1.763,2.299],[1.37,2.908],[1.363,2.908],[-0.235,3.521],[-2.661,3.807],[-3.27,3.644],[-3.355,3.613],[-3.349,3.624],[-1.917,4.948],[-0.722,5.354],[3.358,4.425],[5.176,0.656],[4.77,-1.375],[6.898,0.87],[4.076,-2.472],[2.181,-4.999],[3.623,-10.801],[4.828,-8.868],[6.088,-7.703],[6.41,-8.892],[7.081,-7.074],[8.422,-5.33],[8.665,-4.985],[9.245,-3.991],[9.879,-2.359],[10.13,-0.982]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[-0.03,-0.196],[0.001,-0.315],[0,-0.044],[0.784,-1.26],[0.037,-0.059],[0.129,-0.308],[0.711,-0.509],[0.55,-0.259],[0,0],[0,0],[0.07,-0.029],[0.18,-0.163],[3.652,0],[0.988,0.296],[-0.036,-0.014],[0.11,0.04],[0.723,0.489],[0.845,1.898],[-0.075,1.505],[0.08,-0.373],[-0.467,1.186],[0.175,-0.384],[-0.528,0.892],[-0.21,0.229],[0,0],[-0.018,0.121],[-0.293,0.65],[0,0],[0,-0.031],[-0.004,0.014],[-0.041,0.083],[-0.067,0.076],[-0.005,0.007],[0.006,-0.047],[0,0.004],[-0.041,0.093],[-0.224,0.154],[0,0],[-0.14,-0.364],[0,0],[-0.016,0],[0,-0.005],[-0.039,-0.068],[-0.052,-0.077],[0,0],[0,0],[-0.157,-0.134],[0,0],[-0.764,-0.289],[-0.118,0.088],[0.005,-0.039],[-0.257,0.109],[-0.001,-0.033],[-0.368,0.071],[0.068,-0.058],[-0.176,0.009],[0.062,-0.026],[0.117,-0.057],[0.035,-0.028],[0.261,-0.684],[0,0],[0,0],[-0.485,-0.077],[-0.041,-0.152],[0,0],[0.006,-0.04],[0.233,-0.198],[0.07,-0.045],[0.28,-0.123],[0.316,-0.196],[0.045,-0.044],[0,-0.005],[-0.011,-0.056],[-0.036,-0.068],[0.009,-0.115],[0.083,-0.192],[0.101,0.051],[0,0],[-0.046,-0.118],[0,0],[0.09,0.05],[0.113,0.049],[0.012,-0.004],[0,0],[0.129,0.064],[0.093,-0.166],[-0.179,-0.324],[-0.259,-0.177],[-0.015,-0.012],[-0.218,-0.109],[-0.068,-0.039],[-0.017,-0.071],[-0.427,-0.474],[-0.451,-0.592],[0.366,0.116],[0,0],[0.762,-0.423],[0.818,0.173],[0.199,0.069],[0,0],[0,0],[-0.562,-0.34],[-0.416,-0.078],[-1.148,0.914],[-0.001,1.467],[0.272,0.642],[-0.488,-0.931],[1.067,0.88],[0.381,1.005],[0,0],[0.043,-1.277],[-0.03,-0.651],[-0.141,0.387],[-0.334,-0.559],[-0.386,-0.63],[-0.079,-0.119],[-0.165,-0.347],[-0.16,-0.563],[-0.039,-0.466]],"o":[[0,0],[0.055,0.31],[0,0.04],[-0.062,1.483],[-0.04,0.067],[0.117,0.313],[-0.218,0.847],[-0.474,0.382],[0,0],[0,0],[-0.072,0.033],[-0.104,0.22],[-0.111,0.139],[-1.032,-0.001],[0.036,0.016],[-0.114,-0.038],[-0.834,-0.257],[-1.778,-1.076],[-0.63,-1.369],[-0.123,0.361],[0,-1.275],[-0.241,0.346],[0.247,-1.007],[0.158,-0.267],[0,0],[-0.004,-0.122],[0.065,-0.71],[0,0],[0.006,-0.012],[0,0.031],[0.026,-0.089],[0.045,-0.091],[0.005,-0.007],[0.005,-0.007],[0,0.017],[0.027,-0.051],[0.09,-0.257],[0,0],[-0.013,0.39],[0,0],[0.014,0.02],[0.003,0.001],[0.04,0.082],[0.055,0.097],[0,0],[0,0],[0.115,0.18],[0,0],[0.797,-0.161],[0.095,-0.112],[0,0.039],[0.171,-0.221],[-0.014,0.03],[0.323,-0.19],[0.048,0],[0.162,-0.07],[0.288,-0.03],[-0.122,0.044],[-0.035,0.029],[-0.613,0.401],[0,0],[0,0],[0.108,0.479],[1.373,0.129],[0,0],[-0.002,0.04],[-0.056,0.301],[-0.062,0.056],[-0.047,0.027],[-0.344,0.141],[-0.053,0.035],[-0.016,0.017],[0.025,0.051],[-0.063,-0.071],[0.04,0.108],[0.007,0.209],[-0.091,-0.068],[0,0],[0.106,0.069],[0.034,0.095],[-0.059,-0.084],[-0.132,-0.078],[-0.012,0.007],[0.032,0.057],[0,0],[-0.171,0.083],[-0.151,0.338],[0.174,0.261],[0.016,0.012],[0.2,0.14],[0.066,0.044],[0.061,0.04],[0.332,1.348],[0.786,0.559],[0.261,0.371],[0,0],[-0.374,-0.124],[-0.603,-0.499],[-0.344,-0.557],[0,0],[0,0],[0.383,0.533],[0.377,0.192],[1.431,0.327],[1.148,-0.914],[0.004,-0.698],[0.904,0.537],[-0.606,-1.77],[-0.858,-0.646],[-1.103,-2.96],[0,0],[0.293,0.889],[0.074,-0.405],[0.11,0.642],[0.512,0.728],[0.083,0.112],[0.221,0.314],[0.262,0.523],[0.128,0.45],[0.127,-0.167]],"v":[[10.333,-1.238],[10.393,-0.927],[10.475,0.011],[10.475,0.135],[9.184,4.32],[9.068,4.509],[9.048,5.476],[7.611,7.575],[6.069,8.54],[5.987,8.579],[5.942,8.599],[5.729,8.692],[5.298,9.271],[0.329,10.801],[-2.718,10.353],[-2.614,10.398],[-2.948,10.281],[-5.296,9.156],[-9.325,4.587],[-10.17,0.211],[-10.162,1.314],[-9.767,-2.41],[-10.185,-1.312],[-9.224,-4.176],[-8.671,-4.921],[-8.671,-4.932],[-8.65,-5.296],[-8.109,-6.934],[-8.094,-6.963],[-8.094,-6.888],[-8.094,-6.845],[-7.992,-7.103],[-7.822,-7.355],[-7.809,-7.375],[-7.831,-7.239],[-7.831,-7.216],[-7.736,-7.41],[-7.253,-8.042],[-7.242,-8.048],[-7.05,-6.906],[-7.05,-6.901],[-7.009,-6.957],[-7,-6.949],[-6.881,-6.72],[-6.721,-6.458],[-6.708,-6.443],[-6.697,-6.446],[-6.282,-6.389],[-6.275,-6.383],[-3.888,-6.184],[-3.568,-6.485],[-3.575,-6.367],[-2.922,-6.662],[-2.942,-6.568],[-1.898,-6.676],[-2.121,-6.54],[-1.609,-6.661],[-1.058,-6.541],[-1.416,-6.389],[-1.311,-6.368],[-2.655,-5.193],[-2.655,-5.182],[-2.655,-5.188],[-1.674,-4.268],[-0.31,-3.872],[-0.31,-3.801],[-0.321,-3.681],[-0.768,-2.909],[-0.967,-2.756],[-1.526,-2.513],[-2.206,-2.008],[-2.352,-1.889],[-2.404,-1.82],[-2.351,-1.658],[-2.434,-1.635],[-2.387,-1.296],[-1.761,-0.752],[-2.049,-0.931],[-2.06,-0.931],[-1.826,-0.644],[-1.838,-0.527],[-2.064,-0.73],[-1.967,-1.036],[-2,-1.028],[-1.909,-0.863],[-2.119,-0.975],[-2.525,-0.593],[-2.48,0.459],[-2.346,1.122],[-2.481,1.087],[-2.135,1.462],[-2.284,1.447],[-2.097,1.557],[-1.721,2.102],[-0.49,2.763],[-0.883,3.372],[-0.889,3.372],[-1.895,2.891],[-2.101,2.604],[-2.066,2.045],[-2.151,2.014],[-2.144,2.025],[-1.357,2.808],[-0.162,3.214],[2.435,2.414],[3.385,0.876],[2.979,-1.155],[4.378,1.091],[2.001,-2.291],[2.181,-4.999],[4.248,-10.801],[5.453,-8.868],[5.359,-7.703],[5.264,-8.892],[7.081,-7.074],[8.422,-5.33],[8.665,-4.985],[9.245,-3.991],[9.879,-2.359],[10.13,-0.982]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":37,"s":[{"i":[[0,0],[-0.03,-0.196],[0.001,-0.315],[0,-0.044],[0.784,-1.26],[0.037,-0.059],[0.129,-0.308],[0.711,-0.509],[0.55,-0.259],[0,0],[0,0],[0.07,-0.029],[0.18,-0.163],[3.652,0],[0.988,0.296],[-0.036,-0.014],[0.11,0.04],[0.723,0.489],[0.845,1.898],[-0.075,1.505],[0.08,-0.373],[-0.467,1.186],[0.175,-0.384],[-0.528,0.892],[-0.21,0.229],[0,0],[-0.018,0.121],[-0.293,0.65],[0,0],[0,-0.031],[-0.004,0.014],[-0.041,0.083],[-0.067,0.076],[-0.005,0.007],[0.006,-0.047],[0,0.004],[-0.041,0.093],[-0.224,0.154],[0,0],[-0.14,-0.364],[0,0],[-0.016,0],[0,-0.005],[-0.039,-0.068],[-0.052,-0.077],[0,0],[0,0],[-0.157,-0.134],[0,0],[-0.764,-0.289],[-0.118,0.088],[0.005,-0.039],[-0.257,0.109],[-0.001,-0.033],[-0.368,0.071],[0.068,-0.058],[-0.176,0.009],[0.062,-0.026],[0.117,-0.057],[0.035,-0.028],[0.261,-0.684],[0,0],[0,0],[-0.485,-0.077],[-0.041,-0.152],[0,0],[0.006,-0.04],[0.233,-0.198],[0.07,-0.045],[0.28,-0.123],[0.316,-0.196],[0.045,-0.044],[0,-0.005],[-0.011,-0.056],[-0.036,-0.068],[0.009,-0.115],[0.083,-0.192],[0.101,0.051],[0,0],[-0.046,-0.118],[0,0],[0.09,0.05],[0.113,0.049],[0.012,-0.004],[0,0],[0.129,0.064],[0.093,-0.166],[-0.179,-0.324],[-0.259,-0.177],[-0.015,-0.012],[-0.218,-0.109],[-0.068,-0.039],[-0.017,-0.071],[-0.427,-0.474],[-0.451,-0.592],[0.366,0.116],[0,0],[0.762,-0.423],[0.818,0.173],[0.199,0.069],[0,0],[0,0],[-0.562,-0.34],[-0.416,-0.078],[-1.148,0.914],[-0.001,1.467],[0.272,0.642],[-0.488,-0.931],[1.067,0.88],[0.381,1.005],[0,0],[0.043,-1.277],[-0.03,-0.651],[-0.141,0.387],[-0.334,-0.559],[-0.386,-0.63],[-0.079,-0.119],[-0.165,-0.347],[-0.16,-0.563],[-0.039,-0.466]],"o":[[0,0],[0.055,0.31],[0,0.04],[-0.062,1.483],[-0.04,0.067],[0.117,0.313],[-0.218,0.847],[-0.474,0.382],[0,0],[0,0],[-0.072,0.033],[-0.104,0.22],[-0.111,0.139],[-1.032,-0.001],[0.036,0.016],[-0.114,-0.038],[-0.834,-0.257],[-1.778,-1.076],[-0.63,-1.369],[-0.123,0.361],[0,-1.275],[-0.241,0.346],[0.247,-1.007],[0.158,-0.267],[0,0],[-0.004,-0.122],[0.065,-0.71],[0,0],[0.006,-0.012],[0,0.031],[0.026,-0.089],[0.045,-0.091],[0.005,-0.007],[0.005,-0.007],[0,0.017],[0.027,-0.051],[0.09,-0.257],[0,0],[-0.013,0.39],[0,0],[0.014,0.02],[0.003,0.001],[0.04,0.082],[0.055,0.097],[0,0],[0,0],[0.115,0.18],[0,0],[0.797,-0.161],[0.095,-0.112],[0,0.039],[0.171,-0.221],[-0.014,0.03],[0.323,-0.19],[0.048,0],[0.162,-0.07],[0.288,-0.03],[-0.122,0.044],[-0.035,0.029],[-0.613,0.401],[0,0],[0,0],[0.108,0.479],[1.373,0.129],[0,0],[-0.002,0.04],[-0.056,0.301],[-0.062,0.056],[-0.047,0.027],[-0.344,0.141],[-0.053,0.035],[-0.016,0.017],[0.025,0.051],[-0.063,-0.071],[0.04,0.108],[0.007,0.209],[-0.091,-0.068],[0,0],[0.106,0.069],[0.034,0.095],[-0.059,-0.084],[-0.132,-0.078],[-0.012,0.007],[0.032,0.057],[0,0],[-0.171,0.083],[-0.151,0.338],[0.174,0.261],[0.016,0.012],[0.2,0.14],[0.066,0.044],[0.061,0.04],[0.332,1.348],[0.786,0.559],[0.261,0.371],[0,0],[-0.374,-0.124],[-0.603,-0.499],[-0.344,-0.557],[0,0],[0,0],[0.383,0.533],[0.377,0.192],[1.431,0.327],[1.148,-0.914],[0.004,-0.698],[0.904,0.537],[-0.606,-1.77],[-0.858,-0.646],[-1.103,-2.96],[0,0],[0.293,0.889],[0.074,-0.405],[0.11,0.642],[0.512,0.728],[0.083,0.112],[0.221,0.314],[0.262,0.523],[0.128,0.45],[0.127,-0.167]],"v":[[10.333,-1.238],[10.393,-0.927],[10.475,0.011],[10.475,0.135],[9.184,4.32],[9.068,4.509],[9.048,5.476],[7.611,7.575],[6.069,8.54],[5.987,8.579],[5.942,8.599],[5.729,8.692],[5.298,9.271],[0.329,10.801],[-2.718,10.353],[-2.614,10.398],[-2.948,10.281],[-5.296,9.156],[-9.325,4.587],[-10.17,0.211],[-10.162,1.314],[-9.767,-2.41],[-10.185,-1.312],[-9.224,-4.176],[-8.671,-4.921],[-8.671,-4.932],[-8.65,-5.296],[-8.109,-6.934],[-8.094,-6.963],[-8.094,-6.888],[-8.094,-6.845],[-7.992,-7.103],[-7.822,-7.355],[-7.809,-7.375],[-7.831,-7.239],[-7.831,-7.216],[-7.736,-7.41],[-7.253,-8.042],[-7.242,-8.048],[-7.05,-6.906],[-7.05,-6.901],[-7.009,-6.957],[-7,-6.949],[-6.881,-6.72],[-6.721,-6.458],[-6.708,-6.443],[-6.697,-6.446],[-6.282,-6.389],[-6.275,-6.383],[-3.888,-6.184],[-3.568,-6.485],[-3.575,-6.367],[-2.922,-6.662],[-2.942,-6.568],[-1.898,-6.676],[-2.121,-6.54],[-1.609,-6.661],[-1.058,-6.541],[-1.416,-6.389],[-1.311,-6.368],[-2.655,-5.193],[-2.655,-5.182],[-2.655,-5.188],[-1.674,-4.268],[-0.31,-3.872],[-0.31,-3.801],[-0.321,-3.681],[-0.768,-2.909],[-0.967,-2.756],[-1.526,-2.513],[-2.206,-2.008],[-2.352,-1.889],[-2.404,-1.82],[-2.351,-1.658],[-2.434,-1.635],[-2.387,-1.296],[-1.761,-0.752],[-2.049,-0.931],[-2.06,-0.931],[-1.826,-0.644],[-1.838,-0.527],[-2.064,-0.73],[-1.967,-1.036],[-2,-1.028],[-1.909,-0.863],[-2.119,-0.975],[-2.525,-0.593],[-2.48,0.459],[-2.346,1.122],[-2.481,1.087],[-2.135,1.462],[-2.284,1.447],[-2.097,1.557],[-1.721,2.102],[-0.49,2.763],[-0.883,3.372],[-0.889,3.372],[-1.895,2.891],[-2.101,2.604],[-2.066,2.045],[-2.151,2.014],[-2.144,2.025],[-1.357,2.808],[-0.162,3.214],[2.435,2.414],[3.385,0.876],[2.979,-1.155],[4.378,1.091],[2.001,-2.291],[2.181,-4.999],[4.248,-10.801],[5.453,-8.868],[5.359,-7.703],[5.264,-8.892],[7.081,-7.074],[8.422,-5.33],[8.665,-4.985],[9.245,-3.991],[9.879,-2.359],[10.13,-0.982]],"c":true}]},{"t":60,"s":[{"i":[[0,0],[-0.03,-0.196],[0.001,-0.315],[0,-0.044],[0.784,-1.26],[0.037,-0.059],[0.129,-0.308],[0.711,-0.509],[0.55,-0.259],[0,0],[0,0],[0.07,-0.029],[0.18,-0.163],[3.652,0],[0.988,0.296],[-0.036,-0.014],[0.11,0.04],[0.723,0.489],[0.845,1.898],[-0.075,1.505],[0.08,-0.373],[-0.467,1.186],[0.175,-0.384],[-0.528,0.892],[-0.21,0.229],[0,0],[-0.018,0.121],[-0.293,0.65],[0,0],[0,-0.031],[-0.004,0.014],[-0.041,0.083],[-0.067,0.076],[-0.005,0.007],[0.006,-0.047],[0,0.004],[-0.041,0.093],[-0.224,0.154],[0,0],[-0.14,-0.364],[0,0],[-0.016,0],[0,-0.005],[-0.039,-0.068],[-0.052,-0.077],[0,0],[0,0],[-0.157,-0.134],[0,0],[-0.764,-0.289],[-0.118,0.088],[0.005,-0.039],[-0.257,0.109],[-0.001,-0.033],[-0.368,0.071],[0.068,-0.058],[-0.176,0.009],[0.062,-0.026],[0.117,-0.057],[0.035,-0.028],[0.261,-0.684],[0,0],[0,0],[-0.485,-0.077],[-0.041,-0.152],[0,0],[0.006,-0.04],[0.233,-0.198],[0.07,-0.045],[0.28,-0.123],[0.316,-0.196],[0.045,-0.044],[0,-0.005],[-0.011,-0.056],[-0.036,-0.068],[0.009,-0.115],[0.083,-0.192],[0.101,0.051],[0,0],[-0.046,-0.118],[0,0],[0.09,0.05],[0.113,0.049],[0.012,-0.004],[0,0],[0.129,0.064],[0.093,-0.166],[-0.179,-0.324],[-0.259,-0.177],[-0.015,-0.012],[-0.218,-0.109],[-0.068,-0.039],[-0.064,-0.034],[-0.763,0.111],[-0.451,-0.592],[0.366,0.116],[0,0],[0.762,-0.423],[0.818,0.173],[0.199,0.069],[0,0],[0,0],[-0.562,-0.34],[-0.416,-0.078],[-1.148,0.914],[-0.001,1.467],[0.272,0.642],[-0.488,-0.931],[1.067,0.88],[0.381,1.005],[0,0],[-1.363,-1.273],[-0.526,-0.531],[-0.141,0.387],[-0.334,-0.559],[-0.386,-0.63],[-0.079,-0.119],[-0.165,-0.347],[-0.16,-0.563],[-0.039,-0.466]],"o":[[0,0],[0.055,0.31],[0,0.04],[-0.062,1.483],[-0.04,0.067],[0.117,0.313],[-0.218,0.847],[-0.474,0.382],[0,0],[0,0],[-0.072,0.033],[-0.104,0.22],[-0.111,0.139],[-1.032,-0.001],[0.036,0.016],[-0.114,-0.038],[-0.834,-0.257],[-1.778,-1.076],[-0.63,-1.369],[-0.123,0.361],[0,-1.275],[-0.241,0.346],[0.247,-1.007],[0.158,-0.267],[0,0],[-0.004,-0.122],[0.065,-0.71],[0,0],[0.006,-0.012],[0,0.031],[0.026,-0.089],[0.045,-0.091],[0.005,-0.007],[0.005,-0.007],[0,0.017],[0.027,-0.051],[0.09,-0.257],[0,0],[-0.013,0.39],[0,0],[0.014,0.02],[0.003,0.001],[0.04,0.082],[0.055,0.097],[0,0],[0,0],[0.115,0.18],[0,0],[0.797,-0.161],[0.095,-0.112],[0,0.039],[0.171,-0.221],[-0.014,0.03],[0.323,-0.19],[0.048,0],[0.162,-0.07],[0.288,-0.03],[-0.122,0.044],[-0.035,0.029],[-0.613,0.401],[0,0],[0,0],[0.108,0.479],[1.373,0.129],[0,0],[-0.002,0.04],[-0.056,0.301],[-0.062,0.056],[-0.047,0.027],[-0.344,0.141],[-0.053,0.035],[-0.016,0.017],[0.025,0.051],[-0.063,-0.071],[0.04,0.108],[0.007,0.209],[-0.091,-0.068],[0,0],[0.106,0.069],[0.034,0.095],[-0.059,-0.084],[-0.132,-0.078],[-0.012,0.007],[0.032,0.057],[0,0],[-0.171,0.083],[-0.151,0.338],[0.174,0.261],[0.016,0.012],[0.2,0.14],[0.066,0.044],[0.061,0.04],[1,0.525],[0.733,-0.129],[0.261,0.371],[0,0],[-0.374,-0.124],[-0.755,0.359],[-0.207,-0.04],[0,0],[0,0],[0.383,0.533],[0.377,0.192],[1.431,0.327],[1.148,-0.914],[0.004,-0.698],[0.904,0.537],[-0.606,-1.77],[-0.858,-0.646],[-1.103,-2.96],[0,0],[0.276,0.257],[0.074,-0.405],[0.11,0.642],[0.512,0.728],[0.083,0.112],[0.221,0.314],[0.262,0.523],[0.128,0.45],[0.127,-0.167]],"v":[[10.333,-1.238],[10.393,-0.927],[10.475,0.011],[10.475,0.135],[9.184,4.32],[9.068,4.509],[9.048,5.476],[7.611,7.575],[6.069,8.54],[5.987,8.579],[5.942,8.599],[5.729,8.692],[5.298,9.271],[0.329,10.801],[-2.718,10.353],[-2.614,10.398],[-2.948,10.281],[-5.296,9.156],[-9.325,4.587],[-10.17,0.211],[-10.475,1.314],[-9.767,-2.41],[-10.394,-1.312],[-9.224,-4.176],[-8.671,-4.921],[-8.671,-4.932],[-8.65,-5.296],[-8.109,-7.351],[-8.094,-7.38],[-8.094,-7.305],[-8.094,-7.261],[-7.992,-7.52],[-7.822,-7.772],[-7.809,-7.791],[-7.831,-7.656],[-7.831,-7.632],[-7.736,-7.827],[-7.253,-8.459],[-7.242,-8.464],[-7.05,-7.322],[-7.05,-7.318],[-7.009,-7.374],[-7,-7.366],[-6.881,-7.137],[-6.721,-6.875],[-6.708,-6.86],[-6.697,-6.863],[-6.282,-6.389],[-6.275,-6.383],[-3.888,-6.184],[-3.568,-6.485],[-3.575,-6.367],[-2.922,-6.871],[-2.942,-6.776],[-1.898,-7.171],[-2.121,-7.035],[-1.609,-7.156],[-1.058,-7.036],[-1.416,-6.884],[-1.311,-6.863],[-2.655,-5.193],[-2.655,-5.182],[-2.655,-5.188],[-1.674,-4.268],[0.003,-3.872],[0.003,-3.801],[-0.009,-3.681],[-0.456,-2.909],[-0.654,-2.756],[-1.214,-2.513],[-2.206,-2.008],[-2.352,-1.889],[-2.404,-1.82],[-2.351,-1.658],[-2.434,-1.635],[-2.387,-1.296],[-2.502,-0.687],[-2.791,-0.866],[-2.802,-0.866],[-2.567,-0.579],[-2.579,-0.462],[-2.806,-0.665],[-3.301,-0.919],[-3.334,-0.912],[-3.244,-0.746],[-3.453,-0.858],[-3.859,-0.476],[-3.814,0.576],[-3.159,1.239],[-3.294,1.203],[-2.666,1.578],[-2.815,1.564],[-2.628,1.674],[-0.165,1.546],[1.763,2.299],[1.37,2.908],[1.363,2.908],[-0.235,3.521],[-2.661,3.807],[-3.27,3.644],[-3.355,3.613],[-3.349,3.624],[-1.917,4.948],[-0.722,5.354],[3.358,4.425],[5.176,0.656],[4.77,-1.375],[6.898,0.87],[4.076,-2.472],[2.181,-4.999],[3.623,-10.801],[4.828,-8.868],[6.088,-7.703],[6.41,-8.892],[7.081,-7.074],[8.422,-5.33],[8.665,-4.985],[9.245,-3.991],[9.879,-2.359],[10.13,-0.982]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-0.02,0.05]],"o":[[0.018,-0.051],[0,0]],"v":[[-2.71,-5.03],[-2.655,-5.182]],"c":false},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Vector","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[1090]},{"t":50,"s":[1080]}],"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[16.667,16.667,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[11.667,11.667,100]},{"t":40,"s":[16.667,16.667,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":120,"st":-60,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_web_firefox.png b/Telegram/Resources/icons/settings/devices/device_web_firefox.png new file mode 100644 index 0000000000000000000000000000000000000000..aba0ee2de12262a6b09224e8cf9271b0443e461f GIT binary patch literal 894 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{Hv<FH3r`ovkO<DV zVZPo*fg*apjYPaAF!gTlP3Tq->eNxv5@2E#pQw>{=cjN}+1jY-+~!_+LCs~kw^Z3K zvS*|UD2X}lKEfXowD^1Ni$hP1!rR@H?;SnkG_Uwv@jT<=v~xSZeK|4x4&zSdBKEqi z%(eERxy8kwf#B`ix64^nU#z(L>iYHTE=mhSv{aYWPF{62>*&n5_21uLFRiMwva;H> zVS~cC=>nk|A|ESamS66CA|o$9`O}ZHPm9{y+m%u>7w+6?+2OYMqJ4aLef|FP&v)<I zb?e5B1y23_{ab7f{QkZBX3nyU>keGICZ-*`G9=;9$*#D#xJ@z-o<C3Tbvs-XmE`KJ z|2if*I@x!s7pri*<m)5a>Anw6o;>NGpitmtb}{i_vf$>h)pO^}dGqd_kZVoe&zgDZ zdb+xvdY-~n)z$KHautEEEG;Y+JefLmYSvaSXO<&LhF%kR($doG?Ce;&j`*lee*OA2 zldFyPHNMm9ufINg_%PG0^0Qa3hK6+?<!fKOl<S@5<dYkGR$sj}$#YK9d^xwi^Luve zFp%I$PfxGmkeht6Me#_<g%zitE}Frf^z`GqpDk{S8;=I*i2W3>Ss8LFZ~NwpX`63? z^gcB_Tl4JGry>K1BR>+Q&z(EB$T`Agid)&gBS%=iF1ei9wX|{NO3iP&n<T1x-^g2> zKYxDZyMO=eyqV)-VwMDEGMqhbsWu_`Y|zRrn>QQV+xwR+U%vdMt>CS<Z`W>_#I)`H z`}Eg>HTCuRn{OJVH8(d;=@*@SHjOo<|F|>fT!zILJM6e!6650IlaIBvwJBH`88tC~ zfBLj_d2erTY0%WOH}BnhR@%v4pMUCU(e4+QF9*lRU;n?1jg9RC*X^qr&yUZwoa^_^ z(CT>A-ha=YEsIsX7q205C$IeZ*B?G>-Ys6Nyie$Tr;F0#$B*|oZI?TvxG&|Sv4IR< zvTSLzr;5CJ<7YJyuEe<uPg)2W{w*&rudc2Z>vmNM<U1)l!CImGLH&yZ_U*AUzS-Rg QlAyHj>FVdQ&MBb@0LvYW7ytkO literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_firefox@2x.png b/Telegram/Resources/icons/settings/devices/device_web_firefox@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d7a177b83ea04de7dd74eb6769c744046aa271c2 GIT binary patch literal 1744 zcmZ8ieLNG08{dqXx4cFpc3whPnYTNkVas_L=B0VL?i}haX6_8B$xB``YD9Jj$%<HB zCT1aTF_yO}d6y-sV<T~5Ufaz-zu)KXkMHySKF{<0_xV0IJur@P((2Lx06@;!$=*xE z*aJYsM3o|ymM0=$l$Rq4Kp#5F6E*g~`8i*5a|3)U(hvYJ4F?cEu!x{80ssJ_0s$Zq z0}p0YvHxwcRM7wRfw3`!V+R0$t(@(VSR(M#c^4;de-)#Veyze=`s(F0L$@5P&f&FG zoQ|}WLR_GnD{2_|P{+yBT<Ngnq05(SJQZ|Ck>3%ha$YKC%Rx5_3&Z;a>%#ffk`K%A zn_NL}v*4@kE@s6BU)#3X*v=5x;A^5V#Y(oFiGg;;-yBhpg@DCD$-sZNNkT9v_@u(6 z&`|#P_&A?mDRE&$EvKkRV<us9xuAW-uY6;3bF;KmcV=6|;txN4RGWu{q$DefXJKQr zva&*-a8(UyWHL7bW`%p#_jkqy23)#$3k$(Xi9%F=fB#NwP~5=qa71kEZqs_mOAd$I z8l&vLv9ZA}G9OE)QmL7la(FzRqdcGOOEmH~g2R2W*ukNp`-oT{Muev1^z`%vvKStP zO8yJ?PJ1lo$Ga3t=~UBgkeR8eUixf%e9L8?QnbFi`^y(uu#Q;4%1DJf5C|;81P50v z8!$~QEqU+W*&Q-xlvP!^klu`pq@1<Z*VnhSoSK~#e&LPS?d@%^vsh;r1MxF0SF@xA zV^zNOicI#>L~TuVHI2p5y%D>#v=mCn&&w+>FE9IL3kHQgCe@dgp5^pLSQ4>}KyY(o zqacEiu{n?rhi=^>lc5(s8W|G>EB+)+%9Wm;p1$76OzW#F#@VSPQhnM$TSvlvWJE;J zT~mMGix*2}$=v66*pqdWHXslvOePW@t60#xy}TR-Rd9d5zVPzcv12$KE?uT~;d_j@ zmhP+0d3h-*lRK1A+)#;a)?|vZ(PPqLra5{2-e?*6ZEMWgLl(tlRBEC$`P?Lz>vo~- z+6I4d(J)J5CeHG2gHMYmEU&K23=Yc5qbr9dCnuTs37W#3t(Z37F!fbfR$EzFSz8-I zLjC^0g9q9-)`Ewg+;UpIP9VIZGI>1QQ3@8karm!=h0+g!3bPTZ+uJcs94WPJvD;tX zvmXBrgTdnW^=jU?fBg8-bJ+NX`h9nil1*7eJ3DL7MWD^ueUg%r4!H%1N{QL@sIHt* z;~v51Ss3D4_mODf*LbsEtrL6N4I)3M$-}be0KZ|eDlEBr84dB4XO)$ePoMfcb@*Nk z1nR4^LVGkkr;xglx%c$?!twYN4v7S2Z>zyzwH)uv0j&xXjP=lQq;iG+W70R$U4w&z z@PPL0ww!J@+o6VhYGSdK!H5bEhrel<`N_}E$)P?H-cnazuXFs}=0jCGZ-*viu7*nJ zWv$~oU0un@r5sdPSXfk)*={HOQvcYPH@}|2Xd$lYc*_OSB2!SA8xDUOu9}7T`#cBn z_ICyHT4#<xp-HP<sWNkOb3s8t7z~CM$82bzaS`(J@>39IlCmPyi0R^}oqUwP##Y6+ zx&{iqMUVQugd{jsr&A}!$0xyc<-JMIT%O#WdXmBCa=FaLM&CsCHIooM1j65>UnM^u z9x$%@=ENQn(MY2~?qujJ|1eeB(Gl>Vx~cnwn|){bYTmul+FJJ{HqB*ARZ$UHn5#!+ zQJIB>>dd(_e=9$-?_H>>s){M^EWrBskQM>n@%OI4Y!mEFh3gCM%(~Muo?^b%Zy$M{ zKJH=t9SdjR<{sZtex-#lO=&6QpV1QUxGwZz=nj<F#G^I;unO|`FV0{Gf_MP*Rat_c zT5fLc?(Qzw4cWOT5f<lE*WBEf;{6(Fm9D%p$4Vm#j7kW^GH!dkhBd?+k#Nqy8G6Lx zc@td?{>!Sa4ZWGcSbzUQKJ5J0qt05l>wM`yn;fEPO+(HoKI(hoI%4;<A`N~%?CrB? z7sUo18mNR02naZ37p+byDypht)kmBpa->=@?Lw2?4}HqehZwn;_3mza&G^$dd9Po4 z70-}<Y<*Z)J%%w&&2r4RwzZa>T4-_0^25)0hRCtQ;rhhZ`C(jWsMqdjk5aLue)p-Z z8!A8j`2s;2T3t1c1j<C`$Sv(`EYV&QNL6|g#FDHpNr==(0v&M&^a~h7TbxZ6-CRhU zrnM&;fAVx(CJBCSmF1XA6VB7;)1s3;7kJRS^5clnWIf@hH;rU6nXRt2^WSLxcTx*4 bg1-Xyf3L0aIDf(K;F~!AfU&2e0+as%b(K6z literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_firefox@3x.png b/Telegram/Resources/icons/settings/devices/device_web_firefox@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..06ec192d5baf27c0545db3ecbb9ed7ba1af65be1 GIT binary patch literal 2612 zcmb7GXEYqz79L|nyMic53`Qh^Xd$CUCqx@<V)S0F8ok$uE(p;H(S{Jhm>4yB??RNQ zF|Il>`eaDn<j;HS{k%WEv-dt{uXWb>&iAdg6ZBx}bTn)<004ka<Eg3vDTe<66_B*L zD~YX=0-2A2x-y`4oPCE>s5%&HIO^yC9*}Y>09l+f0Q9GY1U3=?0P<oo068g={n;x9 z{`WMznEZeFpGK~%n`!_6^;->9C3qm&K|w&}tUU8T*pFt<A5j~D5BNNkypj@2;<-0~ zl)`Xkb}4;^^Em~0?*}PPZd?qL*Z0o$rj3=5J!07UkwAI7j_&l<%2_bZ%GujH{n*(f z#5*0Ta_yHW+5JvX4uHuTs2xfD@ZT2i?xfwwd;7qzQ72#GbNE&8Ij+#ay|zrV*nTKI zas{aic}8Kbo7veBMx36Rsk3a)6SY%&#$m4eR<X%rxl`^4Ea_3u2CRLj%B7VhNDq;} zEDHTuq!3|>b}27Q5YV`BOUypLOD_H5lHk)5OUE<&5pyqUbJPAfO%m6I)GcC@^l&pW zY76u_cGy7Tp_4B5$6I4`-?DmBAqp~nyY`J8p#<-{g%=4WPm*x+^(xhcdtd9C7$mKJ zT@eF1BgwWEV`x}9!mr+ku>XyQ!wB!6Ra<@z7ec73$?)@2ceLgDeL1PrFTF<3nOu@3 zWHww$|3Q#lUJCpW<F?R{m&|9?5elC>8*O^N%6+`n6`dD+g9}YT%NqLYG6>Z5t=UKX zv_?IZ(|>rQU=i)=x~r}C<vLhmR57yNs#J~6b|TVor7Z|7;U*Pwwm<j5jFFQ8ULfX> zI7K5G6_q;hM$u_IX}s{UlcU!^<AusGG}JYk8XC9df}x&Zw0!ubF%rh1#KJt5BifUO z<n2$seb=bE6sp!mhhdtnMo5D7DztB7WKd2M!WJ#QZ)JlH6#_}@yaG#(7f45gI2Uex zK3GIQdfyg^2iEl-|1$xW)@Q=l=pXmxGV#KC2j@{vsm6kD%EO3fJSknPpCcf0!6(>} zY)1l#_=<`O1Hqz4PASdYa#z2uLcTrMWV(})s}Lby_`)I`2fWS(9keXW@gZGEq9HE1 z#x;QSyV{Mb!k^riBoJ4zilu6=_Sv?js}Xe)aGs|d3n(XFqENa{9`v2z;2Zv#IC_X6 zTbb}u({tVD<4N`n*~7gp(%zec*&-1Ad0`{8%d?OQ!|Pe(>ya{<21hqVER*@Eo``|O z_U1lhum!N%T)fE?KvXPI+az6E7~#>%n#`xIwg0iw?jY1Oa(7(ZZ}&^^>F%9TEiXvG z!J-2~dE#{*o`N|n3P{1ggIv;n-Fp{%cCeH$;Vu?(#Y!B>6gWRvl1!A~&9xgyG4YMv z_g-vthJ6Z}`2Fju6+mg5!I>Y4x;F{gF1aKe6XiF@piOQI+#eyX&qyzj92u#Q>!_s& zo3VU}MAe{u2_jY<@M<F+H<h#f1;(u6y~d{-2_n~L6U<W0oJo>c&oxfQ@7=%`IF9A% zyG0v@Yz{JF{ne9iDLL~Pi?s)z8b}!H;YpI3ED(1!YC#(V;b$Ir<U2`bW{sC$>lyeW z`OIDdw6cXQ4nYW%=jPyRs?i|{nxMA&FQ&=5LZEj>wdPgUNJEzW>UWr9TUUItveaS~ zH8Qm7VJwC`%b-ume#*AF#-N<0q$5ikt0m|oVvb=Dk#nGXok%?YF`zkeMLaJ*>15>( zYWc`|Vm$fLO`;@VP&k2Ma-vA#fY2aqOfgFG%W{if{R8E?B&zfy!<3c8=jo9#(#RRB zoSam;M~p)u=QzSFYPmH)H|HVQ%GbW6n+eOEqd{Avx#50_t(>yY5@MwjNYbUwA9R_0 z9}_flxZZQMi|>pgpV}bVLcplz={XSgR(1zL*|>+7PZ$_>FU!6_+2QBvIJ5cmxxG=0 zCC*Eqgr@#!4mq#2;+U*;+L<ng_2j1i-W*~IvruJ#PE)?T9Z%a#6}P>en)Ow%UvShA zC;TF1*v~@17Hp(i;kP%Jlh?KE`+-|L+Gp00>Tf0bi5qn$rF07*9D8^JI{wJ`J@g*& zbT2MUzQ5eC8)F6ox)4k!i=@1|OTiu1T|gb-!Uf$v=&J^59RB-&i)SCYu~;GeQp#M# zt7l`HRmilgynN>ZX_oRxFUtnq5z@<ErsZX;YcuQ-L2g%A2d^@i9-|fDtFa_GL!(m6 zIwp%F%fF_VJ#h69I7ScI|Dww7zR=)+s5K8d*=Fo^A+gfgAVi_K-W`*0kk|LHr<jjE zakT#xc&z<Ar#}SDcuRGqo_{6>YwBBTg1YasHEK(24fx?vg$<@&g=F~DFIs1g=ZTNZ zK3JtN&ptWBEekAd@5~^hpUj~pX})W*YQlq}5Q)4jUO!#tWPP{w5O%X5U-u(<-|aNh zIA)i1ZNa?g$F!8}0}_Q$KG2ouW0O|@4;kHSTX(mdIOS!gsj%PSK&***l>txSNkiCt zTab?@fhPFV-&5QshC4^McBHT>=IttFoG95AZxyi`%27F7Q-4v|{(Sws{tFZ=o?~O1 z%oad(|N8~+_<Mb|7E!?p-6D_4GF}EV3Fu1^ue~{;mMZ@tM4hD#ZIL=^*1vOo><)Th z&W`u04kzcCiWl}Y?Un0S{fk!n?6Kxh7gVQ4cDp=2EpU|>S4zVn<C`YVg4|k5H{?^# z+*v>&-IAg*APV83@Fn|4^_R-BL$QF{DXIdP!!6-;4koEL4IJG>2I9%65kC@t+2_lQ zllC_wX?W%v6KMZq%KYbu2=+zqMCem=K9`ktlAxNF35+pz!iR8#HAKShR3^15S1D&t zowHCL$4|ZOpK7?JUAJv)!DGkNG}QpNZO%QMZ1q3bbr_l~(L3k4AKj84U-fJ7+{s+s z{2Rr`$HQ&142Ehy*?Pz-F&QiY09Rvi*SRn1TpoG#<hLOpMlfW+0HbF7I}yyWOxGhB zak!?$PLL6Qg<q++Q(xX1Fj4SdrEuRbRY)Kd)UWQ^_9fb6&N>77r6B8URZ^A91!-7o zAAqBZXoZ{eb@lR4SxSZqaO48XK^b91@unAQ?QNdxzz*@ewIW#`4q7@kX#7u(Z<T|g zL$aQ0iojr8ZE-2h+LDvh*_PJ)$mxppPTSMh-1@EfACBDvosK#Pe@js3^3iE)#LcGV zBa*GHK8G*US|M95I=NB<9(%gNu6~~4sk-AB3Zyi#cjH$Zoef>Vp+a0~0tn&IA*y_S z7TfVapS8dE=X%MB)P47&uu%`X5w8woEDEIixvza%RSm!R3HpSw)rD=w6=E?O^C8Iu sHi;E$6_sM`wef#F@%-PEw9az~Vd~2vSFx%{|1+^^Jb|g!DqFw!4>{=KdjJ3c literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_safari.lottie b/Telegram/Resources/icons/settings/devices/device_web_safari.lottie new file mode 100644 index 000000000..5ff6fcbba --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_web_safari.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"safari_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Com 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[185]},{"t":30,"s":[180]}],"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.15,0.205],[-0.233,0.543],[0,0],[0.142,0.444],[0.385,0.123],[1.435,-0.615],[0,0],[0.231,-0.17],[0.15,-0.205],[0.233,-0.543],[0,0],[-0.142,-0.444],[-0.385,-0.123],[-1.435,0.615],[0,0],[-0.231,0.17]],"o":[[0.17,-0.231],[0,0],[0.615,-1.435],[-0.123,-0.385],[-0.444,-0.142],[0,0],[-0.543,0.233],[-0.205,0.15],[-0.17,0.231],[0,0],[-0.615,1.435],[0.123,0.385],[0.444,0.142],[0,0],[0.543,-0.233],[0.205,-0.15]],"v":[[2.611,2.075],[3.13,1.029],[4.28,-1.654],[5.061,-4.251],[4.251,-5.061],[1.654,-4.28],[-1.029,-3.13],[-2.075,-2.611],[-2.611,-2.075],[-3.13,-1.029],[-4.28,1.654],[-5.061,4.251],[-4.251,5.061],[-1.654,4.28],[1.029,3.13],[2.075,2.611]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"d":1,"ty":"el","s":{"a":0,"k":[2.5,2.5],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"mm","mm":3,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Com 2","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Com 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Com 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-10,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_web_safari.png b/Telegram/Resources/icons/settings/devices/device_web_safari.png new file mode 100644 index 0000000000000000000000000000000000000000..6ca985c193f5206ed6ee0238d8350734dec3548e GIT binary patch literal 655 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&EG6PZ!6K2+p_D ze7g=g2(+GMv1!m2EWhXUk)=s4;m&S>Jx(@l4{kB<xUjn;MSa)xsr6@Sj9C@S%B|*V zC^OG>eerHp)%sthORc7M)$d`7V_bh=zLtPUYPQ+z=7S5azup?P)@`xlkGlQ+k1MuD z=}td=H!ob|!d3ZAKWplC-`!^<=VRgFw{AgS-QT+Z6*g%f>~tQtZB|}0`F4uY&in8A zRjzTKYq4;AZnbf$)165YiuxBCnoF+8Tzma>mfw^E>8~4tHl6yDY%~9S_t8bS-?k;* zcv*5uQefWdyk*SyXDmIZXeYxLD021nSC%H83koM^6gUWFZ#-CG<JU0dVA*b2zV@qG zp^h%hM}-BB9xvS$w_a3d=AqctS9>((Gzl?vTr{pP+<a3b!YK7V3%lcopT7+Rbo?5v z0-P95rW;kw@l$8*)_krz%UL&!O~`JpUunHV&x1`r5;scvZoc`ZXy*&HBB@@tUsXRv zV{5kfzKRi?FMM(PIj_o(UoS1*)U$h`&`NjZ+Mky$+uQ8ZZk0QGvs}!abL#&OQ?@zV zFI=tiWe$^VyO&8`l-T?%YlEe;_3m!Dn>U$Js(?#mL2HDMziI4?soL#4mu#>4{ci#? z?o~cK<+=ZJxwDZ2^YL!^?c#qjII@5F$_98%)0PnCYppZ<cfP+YiqrlZR|V@Hrc}`% YY)1ptxWATX%m*a~Pgg&ebxsLQ05FgT*8l(j literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_safari@2x.png b/Telegram/Resources/icons/settings/devices/device_web_safari@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5ac37986cd7cf87eda7265c88f5fe51b6ca73b39 GIT binary patch literal 1288 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an^x>fu+dP#WAFU z@$Ibr`nrxH$M>ppbUL+Wc>ZC%q9PRdB1A+>OY5m_-pXqoamxdQxVF9s5y}z~;uN%( zdOvyl$&^i-tIN{PzKh%WT+BQ@{rnuu`|s!27N?a}SAAK|qrrTt!Ds<C>Vvh!kKoy} zo12@txw(sri!WckJbChDWo6~VhY$Ds`}OPBpFcI_<>f$`!otG3x_|%v#mx@C%Exlz z>({T3A3xr{eS3bte3r)L*1oG%VJp8bT(~f7_190I9x+-vxAv7B_gLz8%>3T%+q*Yz zEUd4e->A8PQ+dXrgc|+T8!QYB3rkByJ2pDVHqTgl==Kb=^z`(_K0CbG(hTJ;U*ECq z+O=yQSJvI%7-euyps=<tnRk0q<CM20GIdOwE?I7WnXrJbt3D{u_59A>o6{uMDSwjh z4)}5>@9~+*l3RX$?RT{}zkmLg;ODXN@xed2+$600c4?)ze$vi6P+wasD=Vw3s|)nP z!%H`A+_-r&Qm(EoZ_~fUf6jc?+4A?{!-aSA`uqErFJt_3=+9#Zul>us&E%H8E}K2; z^y$-AuU-X)g3bQU`sn-93Kz`$%P*~eyFcLP=g*%%d?-juQ{ykb<oD<FZO1zkbVG`! z_oiz+aQXl7<3tYid9`;Q{haQ8kXPQ?u+(Da1a+0mkAc?AojbR&k+DAfME%TVKFWCy zdiEdW09lk);m`P_ySv*ldGh}g|69Kms-0L9_%C~jLSkK=orip?(dU;hGXvJ2J$p9g z?Bwb~6E$s@ebq%pK^BVJn9f%QY<aBrbN<)!W|Pz(`&d?}rq^X<X=UvB`?vPe?w3p( z?%s`EB~r&R^Yxumr@GREo>*LoSm#_(Rds4M`*ztIni34#uI?zAoA!5mDT`$Kop++! z8r(mqm3Du%dCDc6_qt$aP{QBsqA54~R!))qxhdtwX}93=@87-!>9$V4efxH+Rj=lc zcH4`ut2Enf&wB)<T$TL>OesYgvvyy3t5;$9F!2A(cXmwYb~uOJc(n7%#<0}1qJzd^ zF4tV;&$umrx3uqj78679>jw`SgwDqN;)srpUc7j*-7L4`mn9ef4?ehj59_ww_S4yy z{aeaq7W_(cie7$ddeQq|IXO2bKlxUkC-C-7rI$i}LUQxx*666R#eBzl*ov$r^}bk| zJ6=36O-$fZBAal~4)ukT7XIUmy2_KLY*IW!Q9ilw+Z4mo|7V~0zo!1*)zt@Ia`nn4 zGrCwDcyh%s^!#G~f69k!zh~)%Y+t&O+2_Cs_v>@4)PHo=&#V75|NP08ZiDr$vW}D8 zuRoX`A7$|WNPQ>sdYc1f)ra+U8SY<Qzxu%az4}^==hs)q2de+r{rxdZ^ZrZzA_n&g z=clTim|XwJzSJf4`SbZFX1ra%Z<KRpo6o=Qe>)Go{~@BA*a%9^UK|N5!Ds7R68+cr z`}}nM8Trf9O!noy0^aA%!50$$A3oOIyF7hvyVR+Yl@E{Zt}D{>nR73QZ3<(mgJb|U d>V*i~KSrr&t~q~Z3b2DpAx~F7mvv4FO#lsmV{8Bb literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_safari@3x.png b/Telegram/Resources/icons/settings/devices/device_web_safari@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..b7d19c566310ed6745ddb411a10419cad4297465 GIT binary patch literal 1987 zcmbtVdpHw(8y@+P5_1?Eg^9N1<$LQDk!Ve$E!Nn^x1y3EqlO%o$!W!FNDf2J@hN$! zDTi!EZM-o=r&pInqm2yZ5IOa(f4=YU@A`h%bwBt0oc?*9>w13aI1jX@21Ek@0BE|q zq3{Y$_z87og$+A)U`#<uk$5x`&@f;;t#D9*KJJuLrvM0rRtG5E3<0S8EKvZW006M9 zLJ6=<!Ad`470Um~CRA+uul`wRT3~Pj08snI9d*n*R%y1>(dH_~Fp)n2V&1i=sytkZ z*$eaDN`bx3LgCWN()<&VHTWT%*%kl_^!8_JqzgWItILj<70K)S<h}=+!q>U&#ZIA8 z-jUF6^)db2&{==}q`69JqPDg&(nVce$;|YBb@Fa>nh0ASH#9Wx`TUlamBx8b5{VQV zI#WJo!ulawUtC+GGZ>re>yb1XEg@lRd6^LZPUF&fG`dGFm(R`3y?XVEzuh^i(p(J0 z<5o0+iII_u0)gP;$B!~u5ON&Y4@`!sK${Z>K*H*Rf&wO!+1}o+o;pBCbG1kOE(tp( z4-O74EHA$vA0K6T#ub3Tr9MrzFKYsuo13%QY(in26U!k_Dv$2?e5bgWNFr@_<tE$e z+`xMWxOAv241Exb8PU-c0sP4c!$k1+)pGXRp`js}O!oSgr8Wy}m}_veemN^MlgHy( zbmm5QvAU}3;TIb(M??%`I_FlR>9;^fY(O+}WTU@#AoYA+af-9R38ih+U0IiL<qz z*LBmId<wDj^G(UI)nC*;D%*o5F1%s?`XpMy2=`n10GfrV2E{00J#|~;ZKI7(OFVX3 zZ=T$?gP<6rwchA@LVSElS=pZ5yTu<q6k`HPYeyeYt#pg8&9qo_NxHhaR##j91*5ct z(&=lPn_?WWyVed|y&qhD&_``oB$v<k_w$>R%UMxcYagw1Yz-Sd$kq2GM2;<R8c)yP zw{IWSZF)ktd#k@cvr@Aaccz0@@W_lkoZwXmX+e_7<dl>YTDQvQqLt@JXy}#7c*hJ@ zhg;AFF_{t)a-YX@0+4>>KWJzJ+gGuPt#_#>?z9ID4POjTPkpv@N)*rlL_<I6J-)5S z$}4!0=Bd;F%Xt^U5$R}1x_MwPtmw`i%w(DSgknwk`T5n67(*vuZTi#RLz9cCiZ06K z%Z<ZPD+}MgMU%;Q+FMX1AZIk^{wN{HENQAA>W_`2AF{K{nHEM#7@1YA$6F`*05*OC z4X~kM+G1B#+1I(gFbQL>*@{}R!$asmz1BGxT=&|;@6sm{`+$4BsL6`brhb|+lFBK2 za>SuV{x!QS>YRo6(ZlBEZ(qKQ!%$aclEz;59eYagb(^)F-#NFZt}z&A!4TNe($Y>X zEw8xvco8CCJa~2H<;z>&XTw|DZEBz0X1u-iA>6K9l@8>ii(Mm}ZX%2p^Lzj7t~>Cu zhSTm2uFbnA0mTYB1x0mX64j8sD1<Gl)#%D-P7geDqsn-JgA@IxGp)6V9X&jOyJ&#$ zm8H)F`eb|2p8YYi=}(Cp?tTw>le429K>{U6S9&3YEJqafu&r%P@rXXxULQAW!f;6v zlB(uQOLu2v<(y4}i`gbn3rFG_dN3d`>rfb#`Vb3(?+u4m(MHYKh4_*2xjdcx3te9i z^ey9@-E_}V+K5I2(lyGbR*{1$d(DSwmJ4fT9r2|1=Aiq4XE*}}A-wZ}XRzSaQk}t# z&CMZ|LmPC_qSlhM=27hQkMM$?8`}y+e9qOLo@%F<hlI-)f+!S-k<qDYLfC}FyX49b zwy(i{C(7s}yp)#lTzKDpX=~;WgUodJa-6ZWnKK}|)ReHxJ?ghi*MP_vELqYlCT?Fh z{I|p7*?%UA^HCR<GcipBWl)AD^4qLrLHM{NMXe}kBP(X$B;?&+72=K#GBsm<e%|rY zzOwUUCF00U?PmMWUyDCZ1uV~jUYJe>d$WyRCCBEiib=*`dpiq^s?zByJy^7>l@6xO zG8b~f+8a<X)@XrAQf5>MoS*ynY0{+yLZOfv;n2rkWy8ez8(vI;|7sE0c*RL{9aMvj zVOGT5w)1_r#>%Ej3%<bNfw%3ZiaPpZ>SHCQ5nNrG&OBK*m>N?2?i^RB4;#)fM)aFZ znH^2)fUK`#MaKmpIkiE%NMjs<*3y-_I31R&HScCVx+t!2rR}q2@AHPd@dvDtPYEo| zwS_N~T}_#Ic(&DkJzs+~&&gw(^_BY>3v>-8^ky<Q&q8hLWDWOsx5_^&O>M?|m7d~X z{541gcJCGS7-I3#H?c`^d{Z0d+I1fFYmg6cadHpsg3Bphc;AK^hC3)GTH7Fy@VZ|+ zN8dxJFHRn>y1FNKnmnd@wdVSZ+{UC%q<&wjbiatsDZYEp<@EGBVLXi#;RB<DLTkdD z1yHVFW?sXK{*P$T$gaSH@B2<VA+?#E3%s|MP6C2O?rG#vtr2ff8};YK&)C3c>uemx sGZ+ki|8j+R(}mFde|{tNq$TwYHNF9qajfMr6rgzSt{$ic<hkU(0Y$l<O8@`> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index 437895e44..13e96d5ea 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -68,6 +68,20 @@ <file alias="recording/info_video_landscape.svg">../../art/recording/recording_info_video_landscape.svg</file> <file alias="recording/info_video_portrait.svg">../../art/recording/recording_info_video_portrait.svg</file> </qresource> + <qresource prefix="/icons"> + <file alias="calls/hands.lottie">../../icons/calls/hands.lottie</file> + <file alias="calls/voice.lottie">../../icons/calls/voice.lottie</file> + <file alias="settings/devices/device_desktop_mac.lottie">../../icons/settings/devices/device_desktop_mac.lottie</file> + <file alias="settings/devices/device_desktop_win.lottie">../../icons/settings/devices/device_desktop_win.lottie</file> + <file alias="settings/devices/device_linux.lottie">../../icons/settings/devices/device_linux.lottie</file> + <file alias="settings/devices/device_phone_android.lottie">../../icons/settings/devices/device_phone_android.lottie</file> + <file alias="settings/devices/device_phone_ios.lottie">../../icons/settings/devices/device_phone_ios.lottie</file> + <file alias="settings/devices/device_tablet_ios.lottie">../../icons/settings/devices/device_tablet_ios.lottie</file> + <file alias="settings/devices/device_web_chrome.lottie">../../icons/settings/devices/device_web_chrome.lottie</file> + <file alias="settings/devices/device_web_edge.lottie">../../icons/settings/devices/device_web_edge.lottie</file> + <file alias="settings/devices/device_web_firefox.lottie">../../icons/settings/devices/device_web_firefox.lottie</file> + <file alias="settings/devices/device_web_safari.lottie">../../icons/settings/devices/device_web_safari.lottie</file> + </qresource> <qresource prefix="/qt-project.org"> <file>../qmime/freedesktop.org.xml</file> </qresource> diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp index 9084f0faa..523e22a04 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp @@ -590,13 +590,13 @@ void CallMuteButton::refreshLabels() { void CallMuteButton::refreshIcons() { _icons[0].emplace(Lottie::IconDescriptor{ - .path = u":/gui/icons/calls/voice.lottie"_q, + .path = u":/icons/calls/voice.lottie"_q, .color = st::groupCallIconFg, .sizeOverride = _st->lottieSize, .frame = (_iconState.index ? 0 : _iconState.frameTo), }); _icons[1].emplace(Lottie::IconDescriptor{ - .path = u":/gui/icons/calls/hands.lottie"_q, + .path = u":/icons/calls/hands.lottie"_q, .color = st::groupCallIconFg, .sizeOverride = _st->lottieSize, .frame = (_iconState.index ? _iconState.frameTo : 0), From 94bec3b5749e010574727dacc4b14ec3550e6915 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 09:29:28 +0400 Subject: [PATCH 135/180] Fix crash in archived stickers loading. Fixes #17297. --- Telegram/SourceFiles/boxes/stickers_box.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index ce2774c99..f7e842087 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -492,7 +492,7 @@ void StickersBox::getArchivedDone( const auto index = archived.indexOf(set->id); if (archived.isEmpty() || index != archived.size() - 1) { changedSets = true; - if (index < archived.size() - 1) { + if (index >= 0 && index < archived.size() - 1) { archived.removeAt(index); } archived.push_back(set->id); From 47074b48d65c881111312c3433f61f62c6f16fc4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 12:36:12 +0400 Subject: [PATCH 136/180] Improve sessions list design. --- .../devices/device_linux_ubuntu.lottie | 1 + .../settings/devices/device_linux_ubuntu.png | Bin 0 -> 757 bytes .../devices/device_linux_ubuntu@2x.png | Bin 0 -> 1548 bytes .../devices/device_linux_ubuntu@3x.png | Bin 0 -> 2294 bytes .../icons/settings/devices/device_other.png | Bin 0 -> 414 bytes .../settings/devices/device_other@2x.png | Bin 0 -> 662 bytes .../settings/devices/device_other@3x.png | Bin 0 -> 984 bytes .../settings/devices/device_other_large.png | Bin 0 -> 611 bytes .../devices/device_other_large@2x.png | Bin 0 -> 1011 bytes .../devices/device_other_large@3x.png | Bin 0 -> 1686 bytes .../settings/devices/device_web_other.png | Bin 0 -> 821 bytes .../settings/devices/device_web_other@2x.png | Bin 0 -> 1566 bytes .../settings/devices/device_web_other@3x.png | Bin 0 -> 2454 bytes .../devices/device_web_other_large.png | Bin 0 -> 1081 bytes .../devices/device_web_other_large@2x.png | Bin 0 -> 2148 bytes .../devices/device_web_other_large@3x.png | Bin 0 -> 3999 bytes .../icons/settings/devices/terminate_all.png | Bin 0 -> 699 bytes .../settings/devices/terminate_all@2x.png | Bin 0 -> 1253 bytes .../settings/devices/terminate_all@3x.png | Bin 0 -> 1904 bytes Telegram/Resources/langs/lang.strings | 10 +- .../SourceFiles/api/api_authorizations.cpp | 8 +- Telegram/SourceFiles/api/api_authorizations.h | 3 +- Telegram/SourceFiles/boxes/boxes.style | 38 +++- Telegram/SourceFiles/boxes/sessions_box.cpp | 207 ++++++++++++++++-- Telegram/SourceFiles/info/info.style | 6 - 25 files changed, 232 insertions(+), 41 deletions(-) create mode 100644 Telegram/Resources/icons/settings/devices/device_linux_ubuntu.lottie create mode 100644 Telegram/Resources/icons/settings/devices/device_linux_ubuntu.png create mode 100644 Telegram/Resources/icons/settings/devices/device_linux_ubuntu@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_linux_ubuntu@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_other.png create mode 100644 Telegram/Resources/icons/settings/devices/device_other@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_other@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_other_large.png create mode 100644 Telegram/Resources/icons/settings/devices/device_other_large@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_other_large@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_other.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_other@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_other@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_other_large.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_other_large@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/device_web_other_large@3x.png create mode 100644 Telegram/Resources/icons/settings/devices/terminate_all.png create mode 100644 Telegram/Resources/icons/settings/devices/terminate_all@2x.png create mode 100644 Telegram/Resources/icons/settings/devices/terminate_all@3x.png diff --git a/Telegram/Resources/icons/settings/devices/device_linux_ubuntu.lottie b/Telegram/Resources/icons/settings/devices/device_linux_ubuntu.lottie new file mode 100644 index 000000000..b4f556719 --- /dev/null +++ b/Telegram/Resources/icons/settings/devices/device_linux_ubuntu.lottie @@ -0,0 +1 @@ +{"v":"5.7.4","fr":60,"ip":0,"op":120,"w":30,"h":30,"nm":"ubuntu_30","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Union","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[13.594,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-4.297,-5.178],[-6.797,-9.508],[-8.234,-8.678],[-5.734,-4.348]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[-8.234,8.678],[-5.734,4.348],[-4.297,5.178],[-6.797,9.508]],"c":true},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3.234,0.83],[8.234,0.83],[8.234,-0.83],[3.234,-0.83]],"c":true},"ix":2},"nm":"Контур 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Union","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Ellipse 24","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,51.962,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[-100,-100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[-70,-70,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[-130,-130,100]},{"t":30,"s":[-100,-100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[4,4],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.66,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":60,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 24","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Ellipse 24","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-60,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[-100,-100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[-70,-70,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[-130,-130,100]},{"t":30,"s":[-100,-100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[4,4],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.66,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 24","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ellipse 25","parent":5,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[30,-51.961,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[-100,-100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[-70,-70,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[-130,-130,100]},{"t":30,"s":[-100,-100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[4,4],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"st","c":{"a":0,"k":[0,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.66,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":-60,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 24","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Ellipse 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[-245]},{"t":30,"s":[-240]}],"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[16.667,16.667,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[16,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Контур эллипса 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[600,600],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Ellipse 3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":-60,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/Telegram/Resources/icons/settings/devices/device_linux_ubuntu.png b/Telegram/Resources/icons/settings/devices/device_linux_ubuntu.png new file mode 100644 index 0000000000000000000000000000000000000000..8cb524953435217bd06b630c7a64e65a85df9013 GIT binary patch literal 757 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{Hv<Dxyr+v}NCfBG zu+yto28w(?*AQdnQrY9<;Zx$pqr!Z2hL6t-Pe}uF;|cCYMa!Pc8ysxu+$fTo)M6m2 zxW2gl?)tL$xvw8jao_&CmRYy@|Fv7$f4|+z*5zye8^H#G{{-1YxLU7f)z;UWOYqpp z`Kt)k*43@M{yJ^*$`CDi(UyxDXP$rFeDh8Bx06plr5JgNX-z%Vdz?G(2&39W4}m!m zI({lbHFong<oMd37OlMg`ec{ia^+98ECF}lmN7RnoC#eWDx{%$Bgbs_-F>&;e)wKv z*Y33N_Z+Xt%IfOZU#kpkW%yW`8l%?6tqm*TfA!Vs$A$=<mnFBJ7CCyazIyKw<9Dr& z(@)E+WZMrrDn9;bvCd4YcU8!$g9!}z0=sU%y_aJ)(`VWJ_sYV-E2mt3X)^olgU74> zYBxmaxbc@5%~UwQ|20eV!JjpL?uTZ--!OmegC!R;TzcgC-RGY#KiP6JWl=nr)AHw! zRdtt@t_+FN5p&de>Zmt8*{Rs#VZn?aT#PFZ^rYXqnqi`$v!nXTw6|s5>}@hBK@SQT z=J_o@(j2z>XwUu~(w!~^ay+I|yfKbuvvarK7QeWqw`iwNU%ZakmbvpgzU+GTwaQlR z{PE+*b=d-H>o3}+P2K;*VDq8LW$`|Ci)vC|1*!z|^Jn^}UJjO*v#BV&*~reXyQ@6U zGQ&w@%l8~hg$ISa(@$-9z5mLNhu?oQ)_&XJSlOP)&6L)4`K8M#t(L!a{XDuNTqzsF zR>$(c;D5v(%HJ#(a>31esZf-)rGtjZsgzA0D}2HXgav*E9?E8W>+X8^R;pxz<d1lX ZAG`{eOs_EqXlw!{IZszVmvv4FO#oICH7Ec8 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_linux_ubuntu@2x.png b/Telegram/Resources/icons/settings/devices/device_linux_ubuntu@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..37a44106e314c0dddfae5b6893abe0a91a5bedca GIT binary patch literal 1548 zcmZ8hX*3&H6izHtf~l?A6H1F(Lc&OCOr4>T2$iV4YAH3+)1azN)KV21YTs&C)Kc0| zZ6t`Tq;|1R)7lSd<)BmRAe5R+{Fyn^AK(4%eee5m-@EU9iS~9F84wr*003lctWl0a zc0PiHh%jbJ#^(wNNOZ&?0k20?z6c8x-p%GF4hMh;a|r-2o&XR#k_Z770ssp-8c z3_MyZ75R79xm5JO{7A^WHEaO@h~KwCU2+ZsuDV~-3B9B!SfMKozydP$@dK1{9<E=? zv1A4RAluAM4V9ZA5w7VGem2}f*F*Jr0{zZ4B!BfF%Gh`~bX_n?ZYHno9xnYhJ5ppy zTr8R&DmL96`o7<zmIlB?Yp~?F62W2!5f$K%Wweq+Q%g%DlexaS>gDB?nwDmT!330H z2~MJw#w}DsBcs1|c4VZbNhDHzLj!tqdwZM56I)MBO^uH~M*8~sGo41`E-qT(!otG` z`uk6T!R1BdL>hxJI6Qn>O|7G&Lr}|0OiEIR;c&P_G|a#N3WW;JJ-bm;R(2yaba!E) zVp1)4Yjg7k9xoo=Ha|Bv3Az0HMrFvHy?co7?F}}I#hRV<^z6AeGCI1xw#E{6(9LAI zI)J@Fm3SP=%F6MesJQsksnZs5VutDruv82ZXz1kRM78@Sk4B^OTXc1GKXfN0f$%Vg zsIV|Zrkb*H1@^K}-I;mukGdB{;B6HZ6t}j92E4B?5?fEBA<WF4m*{8~78WKLHc}1` zAG$8?>=?u0PWR_P(*A*geyK1hv{HmtQY+6H9UZ-X{W?nZAoA+fiK(fnot>TTZWI<P z{ZYfIytLGUE00tRyw~lCP0GnR5ffdCfWs+w{#MupNlDe#)|$)h6+ooZI-%9g%>`gZ zWo6qo;RsHvsGwYgeC5jVUOu0n>nv0@x3+S$vJFj4)>c>Juw=41p8d!W4xgEwRg~7y zRYz=XZB@AXp7680z`kH?oSK}htg6~eCliTI-rni?$I4WrB|xBRD%BK$DBb69IE1ce z+7LZGs_@Kyey9)yOG`_+nWP+AQF5|WGd3zka~v{`0KRQ)J-}yll~b;by??K6udb7u zm&d?GrSyjC(NdtkobrGscde+`O14ztb8rV$p6ty~H^RTg69S#*=H|e=(-RYY{t9C~ zJw4!n+^VykZjmt}j9b5$n|nVEl70Uvuf$(LmPjN{>*Q5IJDYesziy()!s60W9W!7G z<LLOr0t#*K=(uQVN}zfdE(-60!C-tal9iN{oUZvPl5OHZ$}BFfeR$G8Ai8F<ZjjCX zys%J*$l_0~GMUUo!Jg7BpEr&C4(06ZY+62@<l!+hPj4}Jcbn((QqJ03Tudw@=!^d9 z%1Sm{mP{s3=SAn2l;l>Q%-N2OjpeG@`iSM8`_9bd4-O8#9A>i}ZHht2{BQc>ffIdw zeGnaci0`X<D;u<n%ZbjwOO^5Q7(2V4B^R`{wYxeyWm7?Z=V6^QT&~alUBWBOto`z1 zHzX@DAwf3SI<qQBb~Hw#Y-{X3iG+&C<k!}yl<=Px)ki@-RPp<TKjfU2R#FHQ29wt^ z$~d!ep|QOkro>61%s!?QIv9+K0y~^LwTWxfB>FYz9j9AxXYNm0I$h7qmb~kV8S|y6 zfQOdR85O~e%MM|K`{|h&pBL%oCJ~3W2Y92dzP|WkE&o66UAf6B7~R4Nh+wG^jgm~W zn5pS$b`ZhW9VUt0d(%M;KnRB8l?VjFva?k>eE|}|2YE~_RL8^szl%(ak5`9z#>u!; z^||=TZts>p*_$65W5MhqNGKV>-9u}#fp*5`1SdeZJG_nHDpen7p!o%oidMK7+}_f% zKX^bvY;@&lNa(c{0De>D5hc!k^td|WbU*|7NJd6Pl&i$WQYfO$fpPkXkupU#Hz^I- zK3mXKkd>FG&HVaeT2-?;`Pa8$DR3(lT2^u&Z*7zOVAIVjrG1Ir+~RsS{70Ajp9jV~ bLvoLwKR#dr-yV8!<is{;JJf5W&!c|;?iSFZ literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_linux_ubuntu@3x.png b/Telegram/Resources/icons/settings/devices/device_linux_ubuntu@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..8544938c11b0515179f669acb3a07cfe6694d8f4 GIT binary patch literal 2294 zcmbVOc{J4R7a!X+M3zeSC40yilCe!Q*{19ys<Bh@${xm^Y~huC8~awWdx??EAj&Rc zCfNtc&e+BK?VsQ8|M#5xdG39l=bn4cJ?Gxfy)i}xIxI{)Odt@51+A-T0_fmBfY1V` zD-t#hC>k#l9Tcc)fNvSNXgXS;o%HoV*8m#=qKR?_(f@G)zykmTqRXcN(E*y~&s#q2 z|9*q>>Hcs3aTH3sq78(wL2Dw-{Akv4oTCk_`MSC5oEYshlf;CNO)h12!KS1vVQCd2 z#k!U~yc7m!nAT|LsExMN-8424Q8D|w<o#80e+D%2Y=64L1Lw8T?CUh(wNW3q)PFLt zk-O`)uD-H?D>IipO$0H%{%>+ihW*LZhr_HJF6%#<cXn6CeK)4~RiFQLa<J9lGX8go znz)^nxB>29Yrfn1`}9DL>dIIpuU*W1>Eg<G^>k~%zEvxBca^Mg<3@w?NNGP1hg370 zE@$1-)1wi1u(>(g(c-&d)#N_A(3?*CbEx39VU9{f4NBy^PJ#fxf>YmM9>P&<VVeGk zx?JzM(6h5Nc;C$IiCk5<-72Z}1tykLcIt!w{0!PeKT8pPP_~JnF?xviUjBW&FLr0j z&e2g!g8}Kj*q51WTV>UH?ZouW4WD15+uNRoPw`RX)z-ZS+dDfx2z;s6(tzh10wslD zvYj`yJW}><{It+N(G*!Nq&i92TbpQ6e^su*Io#&AW!DkO@=fx2k#;=A$TLKG<ubR3 zfmX820CxA6?88PqDOn+nfIU4ozCIpiAIk7iS3Gb3>DHxI-}$ci)Y0U2a-Cyu{o_H} zFpaO9Bj{fa0sHH+1Y6Npf8XC1aoJm+ls=J24E4Zsb*N#fy4*o!#l?@BJ+-<hI$ce= z;udCR4v<|SxsO4|J*UYi14F}ZHQmL!z(daxN=FoHy81z)arzqtcGj+}3==rGQ;6vz zW>&$HK2DwbMP0G-L4ye!pPJtgI}QmCI`W2dP{p!ux?Pu*O%0z3;8AwZUzL@jG`db` z_-|{1g(*Njk`TPl%A%Km^i?7)caqT@(mE?OwZVq?;&5k4^@7U6sU>5!kbzK>_i_@U z7?K}%;g-~|QvK{pZn*=!>2kdz55!QD&F-`9I2<1)&nS!T+=UDxKipGO3aX8~Qyt1U zKJnhI+PW=x{RdozA>@dPPS~0I6w9yTZB+O*$B%r!!XgZ^m7baTHb9rHpYkQyM;q@% z`J8wsO#T`1T-XJ#;X*A;%X6)rN<V^{4XMT!D8Ji5(GX+XoyGnU_1GGVY*qj5;>`r9 zj43i?&_l+iZSLE<`-M30?DqzjZAk`4um=6UrJpxzL_~z0X2M(Y(e8?Ij<d5fSKC#A zxc%Xuf{0)~H+)k3?WhZfdSbaB78Vwy78I2q59VSz>Iw!)Z6R&KjPG?~l;%3qB2Hg5 zUlzd1%v)qm7ZHgZ@xsEthvRu~rnpr_${{}!2XXMCD_`^B9&?)EV3rC8e8|};i2y4u zL7?&+#FjTjkZz?{2J?~QQ8|d%!*x=veJbbtv+0kRG6VitZisBT>qH~>E$IYkTy3wq z$*O=l)<&(o*wNjtMO@LUk)z8z?@YGr9IJqZ^BliI!l^cM0P~PY!X{W*o5Qe2>~5N0 znyDX`I@a5)H!V9``YY@_BKRcwSYCst9~lm|9bX2zYw@dH*v;}E>~IaKGt4>;?p2tR z7dqGd$mw`*?HP5tEo2&~2T&dC*$?ISYLZxsW%}27Gf5drt~?(dOJ2JJO{S8eHzOT* za!{K~gV|Y|cz>;u$dG)-Qv!4Bxm1F)$Ro<MX(yVCLC1Pin491VFjzT<2Ps(oGC&Pu zRhSm2nw+F$2GkanEUurT=Q@eMcU?Bk^b)JFPvIH0`CMp8?wiF<Ckh>yxygU0H10AC zJPNd8QZf-$^dTj3SNG5v<cUVdB%yUA8kuN6$3=+Ns6@v*b;I(5^5pF!abOv#2*Yky z5pul`d1>$bm_mhwD*`p>!K<W52~t&DXQ%$`e{r+1%c<avU-%|<+4Q*hwDU<nfzL@V zN5$vpVr552hGR8Qa{wPEKDNl#9C>;88{oN3eP#OSQl^#d;KonJ(!+fbB~7+<E`C8* z6NY~FzFi~NpTo*L?dhQ;o@G^x*7RZU-dk}rEfw2NrkzkOmc++|msA^;poV49kC!9Q z^SJ!9-CIcNw7$W^M}jl@W-GZ06|u5q6?0Vh)I%=<)S8#cFN1VoCFMpcq%ffLWW2)# z;co2Q7-K#@)!7-7YIuFugYfX=Kr)$}k*``BrT*$u>=k>kbZ&d3>eY@Rx<y`;U>wxp zGaG_&67SQS@IGU6zVUTF*K-eH;cE?Ia(Qc{S`l#iAhb)Lu%LiQ{i0=$NLf9eHIE4@ z&6W2IAG(<6$O<i-Z)|cb)hH^Gk2W;gYyI?=(agi6Hi(nht=rKylX+%PTfSK3H@}*N z5wC8Rej2JhRnU5%_MMFfE80h=UNGF9a;LFX{x8ZlSN88a+s-f8$7c1p&@b!U>;?a^ zkIU6bV-?h>pzlN#c^1$bbk4R*2w0lm@>j0Sy!S2qsOeGrkP2L54jHuc6!II(M8Yh? z?{aoP&eH1$dVO21N|OFkv8Q&wi)Qa>p3Oi=>;#5Kz_3vY6hvcb?iXjtV;T3qHwPW> zF`Tr2GV+9iJwE)k1<+dLm9Z^$xrhH4RlhT@^nB@JK4(*kYh2>H$}urR3|}w``!c4N zFSE#1CP3XF7@jDnT@P>>ugY2VPIjFPpXF@J2IfQpmwX|1yl}=Upc&U>rS%;D#aLZs zF73(cFaH?qflo34!O!*>kr5^xTgx08$*|O9n=vTZ2RWOEQiPmB#JgTEb^5==@SG}} m%$Z5tMbV1h`Y&S3cEBoe`<Yt3uORi$&Vkl4(5yn)y!aQIgho>U literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_other.png b/Telegram/Resources/icons/settings/devices/device_other.png new file mode 100644 index 0000000000000000000000000000000000000000..ad8bce06a0274608d59104860f93ee49a225fe2a GIT binary patch literal 414 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{H&7Xer;B4q1n1kS zzI-kY0<EcMnvU!f_j@Rz_dv=o;dRs@);Ez|N1{|z#Fja+1YTWqw%_xVTlLk@^}n3A zFZ+0uWd?(A0+UaI+Jjwrfg*=bu3n{e{_`^_@poMJf>)QuzTQ=Lu+qnN=bN)7iY<5k zDe}kn%vz?o{=<}Z(M73|hq}W(js%;T^vl)UKfYRHwR@kp&1s{8&c_uK+ZN{DR^XU& zcWbHcPm3<4)0@5(FJM0PZ}a?nPqHMA>A%&?zp#H})i!mGJ92h&WL#c<pM3GZTp81< zV3~c7Eo$$-Hy7B+bek{ha;fa9=8oIXE9WiKoa+2?Tduk9WR;UuOcm{kKiZn>W2YSV hY2Y+qRQu4*5@&Jk;Jk|xnV^tl@O1TaS?83{1OQ$rmq-8r literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_other@2x.png b/Telegram/Resources/icons/settings/devices/device_other@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..db27d3482c6d9eb8779c1a13957984a987fc386d GIT binary patch literal 662 zcmeAS@N?(olHy`uVBq!ia0vp^At21b1SBVOwoe06jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-evVa-E1}WU0an^x>fr-J>#WAFU z@$Ia=eT;z;ZRV4ETU<OsT$YBtifMTv>eX6ZAS<;rY;iGrNuERVA^!}4);IH9HX2U8 z@!8xs{k+}fvp?>=uX+F3Z_bks9}luJayT$B2{bUUC@^3$8{%13U42zyb1!duykY$D z$AwmNue~k}|H1h@Y1xd}{ag0mf8XnNnDf8V=jj)PcFsKi+;4{egWUd4H8ygsP6sd4 zC#(?vRJ1eap37Ee0hXy=uA!o<wkOzKG2D1N$1Hn?*fU<nWh(!#pL||q>HKDX+b@<U zufJApjCk|?_l2mW>72Hn56yP*->fRy8B?`4j%(VJ>044wZ!N$5_Ei5wztoZpZU1Sf zpI-J~e);Cxviz!fe#<XrOgaBN_{0A+oyq-ehZh=5KAE!a`f4Bfhw)eU?Ed}d^Y1kw zTAq2CZ_94We_rL+b#&3HBX^&K&ds=z9LGFMTximFB{yDSmGz8gxj9c(3odTsoOHe6 zS!>Iay&ZvvTUwulubz7Bke-K*Ov1G+Q}H<&XA%@EdqZU>u9%r`HoN_><FkO1d(sRf zj-2&4TeJGA&~vZC^GmM(ud!Rrb@j~oG~=tRTVBt%Kh(9~_D-JpOtr};U6!3$vF!f) wd^73K+kai>tAG6BR)=a5zW88ZspF_&UOsh((Y_>s<DfL*>FVdQ&MBb@0RDduCIA2c literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_other@3x.png b/Telegram/Resources/icons/settings/devices/device_other@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9ec10ee96fff9617beb21ffc34ac4616eba659eb GIT binary patch literal 984 zcmeAS@N?(olHy`uVBq!ia0vp^bs)^a1SE4EjqU>}#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyz1ATfmH9gA^)E6L$kDck*;`45?sz zduMN!k)ue%Lk&lPMaP^1v>i8W_{!un{n+gfOg{5hcf?+Z-Vn6mLYQ^+{~(qtZUVZO zp8jTIQEV4YEdRD+_4=vlX2s{uY~K9({I1H12aOC&910B-5DESYLY-G$fBjfdV<Gd8 z_07iHZ`%$({QbMyMsB<L<Ajt`Pm7ulDkMDMJ3P~G`BX1ei8M2}#B0?Wclvif`u*E_ zuHWq$<qSnLKKC4E;x4%S{CPUVjU1kXbM7<0d1J8UYgKOYJ@FzJy`1eEZ|+%VXKQ=& z#FFWYBXsnpd&j?atvy@kBWG1^{HDh4{q`QVz4wbhDDOYC+vmmHa+xpZ^tjeu&{nXX z-f-`x>q5~g)|BT$7p!+{Y|*-4ujQkhq5AA|<@VcufBpLP^QWcxwv#DEdBHPo$=a=o zUwJaZT&g$YOWwVp_>+;-?!N#2w8+vY``Yh_r3a@beP&(teP@f#;i)VBPm%3)Q{Vdh z`SY^fd++A`|8w-`gYVzV&1Rd<J{zJ{>Lt{t{#<?O_NQ;(zHPbsvgD4a+5OZupZj-h z-hNwkHOu{1icx0R#@A2M-p(&ucwGJar>OjOk}^v--CVOX;bqj@uV1g0#$Wsx_u}Z5 z*!E{aU9a~oQvSm9#OcM;4#AZR#TBX)zf@gYB=m*JMZYEPd{jtNT*Hgj3$G))y)JMI z>=mf8%?{+OVq7Hdv_4%|%TeFqi_?p*Yg|=duy(|8?47rD1<PKBi`)yZZx)-n0MoEb z3*LXNTBP;RmTQWa>h=BgR%Kh3F5CDjxA*I@PY(54WwYA<J$-ug&XR{89lmX?-Ws)c z*2kHL%3j#p*>SZVnki&{HRoFU+MhQT_8(2!#wPN?ru*ojYaDMc%#L4kTWW%TrBLS) z!_R4(KW-}e{db?ga>-1y$tP#r{#0WdS?yDIdb`<-|9RK4wuY^q>aIM0*Zr5gwtD=k jyA&x&ra-;dSpG3{2|V1Il|J`7C|7y9`njxgN@xNAGaID% literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_other_large.png b/Telegram/Resources/icons/settings/devices/device_other_large.png new file mode 100644 index 0000000000000000000000000000000000000000..0f47128db6ca4b54c75c61c84fd1c3549865e30b GIT binary patch literal 611 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1SGf4^HT*<jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zvw#`F1}W^m|1t)s?5wAYV@L+; z+ZlVi4mpUlo=v;Dr%7SkrUeNLrzYg*zOIf~CVtyQEKGB4j?Nn&Mb7A(KN#9dnv^&M zC*O4x=j>D|o*Y>|-8T5dpE=df&$ufUPd>e{k%6ND3H{M#3hKJNFLt)hY0V^O#rDGo zH?8?wCqGNv=8DN%=ZxEL%ifknZ#bQ>%Wt{zl@H;YzE;WJQQi1|(h3f)sI_4Q8mr=0 zUlnnEc;3x8Rzu`R@MmpR&r{Do|EZdl`ESqtReu;SPd>TiW{z05E8CBIeaDsmTgLxf z>NKVKV1k8An$OpeOS8|WnMfHPyZrJ>>D?Fd7t40PE!*AcUbQOoutClJ_uqf-jeGyl zI#zG`@y8$E8@;vQGd?os=#8%Uy{vXqOM1dD3HeMeNEK)<VXIgYFhT9%DFwEdJRdw3 z)=x5dJ8gS}j@MG5POpHRS!o3`cKI9st;=0$E-X6ZPle9((=TL=?)I!&<y50SJ5AoQ z<;{^v8)lpQww-_8kn?ePe&fkyo5iKdyT4Y={rdd#&!<H@{oAZI-_$V?mMyAHTotx@ zs#ojP;)@lRdY)IVzWS=f%2bLsY@y3*hTGdpW7A!EHFb|DFfbt_7N+^<+2T#iB1}L@ Og2B_(&t;ucLK6Vje)UQK literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_other_large@2x.png b/Telegram/Resources/icons/settings/devices/device_other_large@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ee45e7b9a1cbf56c704535bda059f8ab13a12302 GIT binary patch literal 1011 zcmeAS@N?(olHy`uVBq!ia0vp^6(G#P1SGeyEo=o+jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Jv49!D1}Wrt6#5RRJl502F{Fa= z?QO$sB}a*4A5|n)b1+IPJ9;E8p4;o&rEr6L**ESR5*(SAqJMC<d@<XWq<h3_Nx+1J z<rz~Ci8ygQdDm+FOzhdYir4$zN&JuIeSBu~`+qTM<|m&#Xkcb!5pZB2i?|Wpc=&D1 z&!<mQH%6R!TC{xm^4pe=t<od)rk_85zQ4bJu3!4xkAD4~N0a#Z`R(u6?Yf(1G_&N^ z$J38)z1?>E#QxRQRaIRsO7U@hi<wV7{p6?dU+dGqe{ttOdgw<@TEBjM=;vnM@U>yz z6MuaAG|6vy^V197zkmFgpnmH2@7>2VOWwb5QCb+dCFt45kA*gJ;at~S!?dczlN;|{ z+RkQjAzJ9hrOa<n`{elA&CShk=7inT=KEiJCT+9sLH6od+qZAOP@gdI`qr$iRV826 z=C8c4&~;~k221t#9W%tNw%j?bw~Hk%c*lk2g9mme&6z)cdz5Zu>-CtkDNA~$M>cL| zk(babSsP|NT_q0a&TOIT_LFO5H}HvDO+G1+vD18S&Ht;@cB&n}=(gj6y1M$Y)W4#K z|M8`)JNfbMg}-m-I-IUv{r-{J9F6s-)f|8L+;%@K{WN)(&(CZ5)Bkytq%p32oVAoG zcjAi@2AZ*6IQ6?J&)rn6|Mk556Vt4Ix5X<%yp${Fp7~gj)7HB0u7miknDiID7Z#RI z@4HZYS5txEh1b$Y`TrN}{i$_Bu6)YUNxswhZfb9h&~du+eXG;u-Tm3mr}OP(crW)v zbo$=s@~j(5zWlB=7u>ts^ZDnU@0HAFX8TDolx)6fQs`&8Cx6=Bxbx+?v(J33y|mwd z&Gpyw=g&WAcgoDaIP3WTW&F;}^Upu`UV7>1(N=5kZz)DQ_wF_Acl^oU>9)AAw6wIW zEbUa>#R;O$@1E}7z58X!t|SS0#Rdi@4h7PQ1&SYpWwX_6eqV3$17$c*S3j3^P6<r_ D52&yp literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_other_large@3x.png b/Telegram/Resources/icons/settings/devices/device_other_large@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..5b617f8336dd3551fb9c31077ecdd94cb0e903fd GIT binary patch literal 1686 zcmeAS@N?(olHy`uVBq!ia0vp^TR@nD2}o{QKQR?ZF%}28J29*~C-V}>aY>EvO!M_+ z&;qhK7#Q0#8CXC{fLIEM85o!sFfuR$X-1IP0w%cZmIcfRHb`OE)oGOs3~UUZE{-7; zjBoGm*3XHRIQ}s?&E=qr3&$cw!6v7rlQ`D;EehOa`iEKLLbuoAf@~KhK~AntZxf9Y zVUaw+!dzb!mglwi=0r?PKWEXlHT~L|8q;gHs?Tq}{_o9v+iHo;b$=gmGqMOcFbp{H zAXee=-iPnL|M~N0&mNod<KOR2+`D)0-TU|34?lb_=JELW)-79f^z^#!zMt}M*S>x8 zX3SWzZk^xbVA<0P#l^(dtzW;qMCHrDy?gfb^!BncHAc+2lz;ourAyu2+`ZG~{$9Nr zy8P3_a;>_VGiORmONWMr`t@q%HLqN;f`hF&J3G7ZcCyji73<gQ+pZG~><^k3pOuv* zW0{^8Yx(T?^WaadC$IZ&JO6z0>F1w5eY!6H=<YPWy;Ih%UCZ0*bWb*GQ^>x(dv*2n z$`ZHOiL-r7=b1Wn>N_5_MrBPcty5>u`aWPdvrd-F-O;g8GHjN)%)$Z<nY#Va<_?@Z zE0jg}T9q5sn`8fnHWd7s+S>Y`TY#@f0LXD!n#lT4fjN+s!#zYQ?1Om{zp+BS#?5>8 z;-aE%-Mu^Yd+v$o>bUrL+dK2$UTQhK?M3z8*4Ea>#>UB$Cx2%>`F(D|4f}L{e*U*_ z-)d`XfAc=RUFqS5H?`&E*YDh^si?@<nWuEB&^ask@6Vq*w{A5xousJOayZT6*YDru zWo0>GJ{h`)5_|vu`BPI<W3wqHGV<n`GiRPYUAm{D^B3QS`n3~&&j3cxyLaz;c-rqZ z30H22(|!5&?O*YUmtR*`RVig!Sy?^V6D6~K&+%&$w==oQt!ek3WF_;7o9Aag*V4;3 zZfN}9nPMne@8xOtsQB-LOxL*=^2@$||NgW*MqYl-)TySnwr`I{P6>C8jg6K6{xooQ z!Hq^+TiejqS+iz6$#d4-a`o+-H(NqqO}1)zEIdad>}3A&tJmiJYj0eB^lYO5pYzAX zZFfpn9e@7h$qH4m-SfoEAFIf;HMNN;$V9d56I2+2Y+#}Z4-Zd^Fo_W;c=34c)5C|{ z-P~TiejQuTS;}+#`0<T)NB+K*<!Y~Kd;Q_VhTkbKjh))(g<WE|jqICJpmKVq*8vMo zccu%IQ>TCAa5s>cGk0!nP0f~nj_2Qr$Vf?fO$+|_weZ;EwvwWvO>5V_eVv@-?apGe zzvxGe-OTyNCN55uFyyR|<J<X3{rZ#+!;ee99RnJpA>x(mV!6w~!QQd@m-55R!s1Of z{OxZvS)X~D#9fq{IC<@zJ9k`e|9F1dRyVIryuZN5*Z1TcleFoYH#Iah1CMXrxl_}B zW!A#A>&_fcULEr2H4h)(uFsn;cTYUwV^j2c?Sj-(=K9}MjZG@dCwE)5Sd_ZEyFZ<I ziCf1#%gn??<J6lsZ<?E#cORadz2S`X^0FyhcX#dDwR7jr#fuluKQlSw`mH3>nOn3! zsd(Bw`IVTxZ2RWT#xu?JiuC1lJx^C^*ly~H+qP#<Oxo8;k+Og5>c6bDix%c<f2y=) q>(;MdzI?e^w#Ip2%f-firvHp=F$se2QX`gv>I6?$KbLh*2~7ZE<dpXS literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_other.png b/Telegram/Resources/icons/settings/devices/device_web_other.png new file mode 100644 index 0000000000000000000000000000000000000000..9b777f53fe0f5b5d52167046c5f445b343c0022c GIT binary patch literal 821 zcmeAS@N?(olHy`uVBq!ia0vp^S|H591SGu{#_<3t#^NA%Cx&(BWL^R}E~ycoX}-Q- zRU8bA?U@WLASFO71;h*t%nKM9n1M7SNNfQUTvlrVGlC6LctvU{Hv<FHd`}n0kO<DV zVY|DM90g>r_saOP3E#JB{Kd#scTl3E|3E|{FVB&W+<zDyO$2Ldn;M%l9@tCFT%jR# zriZ7aqyNNpIUNnX-LL1qerK`y;M)g9ze-9&zrTDHU0bYd9?1xVjsBqyg_$OcFG_H= z*4W+u`*&~LdV9J4_3PI!zPRFMj-9RT-aUI_UInb$doW=|*y`PP`4s=(FbL6`UTQTr zr2o>}rh^Z%wk}%Hez@_#?UjF>^rn07zpsDy^Y3Z>{rw_EyYD_L+F4@dt5`orDsjTk z8acKX@4o*|G5WbLUQ_Ib`BAz4<3Jq?14?$s9(hq>bs&S`MH5Te?z@vuwuF6Plv3E~ zQGCQ~_SuMa#~(MAZ}CuZTFtJ&=A57-#@%!9frvruDZcZ!LbOCJnwH!UlT0;|H0VC+ zv@}SNxk1l^N02$$iZ$g?<n`r@8@t=B*;J=`Jx$yE@@v(T6M5USO=f+qy8Hh7&8>$Y z2CTk%A=5xY;AM$bet!N$max@dfBu}QtIyEs!c_h1IGehX;{NWQhxeNTIWFXFKgm-* zOK4Tt>a)*_=T(=Ni}ST#%r%j^{Bp{V_ICChSwF5;rsy~Z1^sPtI%1W4H4`PKw4GV( zZL@Ypg%69Sjq<ki54{)PH+&MyJmY*r_2JE&V=4^gHyKoXMf~eFemKMsekkc-ptr!) zw9SP%cKK_=PJ^Ty)F%lq+$hk!RPL3;x|!Es>rVADX+Q00He1v!>Fnc=6$%0h$xGxI z=5RcYZLoj%=<o6UvK*yv=fAT~S6uqUWpUuHFEK?cUer3AJosvYSA1B3=bR9&(sw%r zyN_nPzFw7gfotEVjjq}geq0gxe|nPkzphvtGa;tQCs|YjqJAvw6*8CMi(VVHH*WtU n=ja_AlBc;&0O6mtO!kat*gn4xXI`@wlw>_!{an^LB{Ts58ZB6D literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_other@2x.png b/Telegram/Resources/icons/settings/devices/device_web_other@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ce90741f832fd5a28b46e96e9124e0dd2f8e687c GIT binary patch literal 1566 zcmaJ>do<Gv9RJPx*{wC@RqkdM5_ufCBJw!KM3`%ZqIt~KR_H-@hvhXigxfqqgj-5& zg)}TK%C*^MbICO)j}0r2k-8z-oqz8Auk$^h@8|pY{P8{K`#s;>(|%ss8u}Ul0BCz7 zJp$#7-hrBe+?S}6sd54(1$wywPq~Kg<%vf$#yjTJDZpGFs{tT#9H6u#kwae&08p#} z0g7@4?bK=%{_CP^6#tKRgl#yED*!0xczc{gCxe#GdYk?e4oTyb+YpkESXh{PGW9gF zY?`9HSm?fNCgV2?NPN@e6M7NU-aea#fkL8lwDjESrQetY?)3?o?KkNcO7&L@hxv&~ z#+S+@sq9@6*~sOSznANGt@{{k_msV@F$~6Hv3z`d1jEC_0>K3=wyv&@zuDW{yR@{l zy}iA>y!`RwM}a`VX5SJvCF0#zS64Y4j$G{T?=N#)`IShltf=?}hjYCW7GI8vFQiZw zrP8jhuIt^=nzuVTI+m9Sl>Mdp@3&YP85zRyaW0Q{WIZbI%8eW20s$BT5fcxG2Vi}C zSaWl~dmAlvza1SdE-49*d}6yW$m6w*PG7oosrXixOKS0u+Fn5GW@V*e?D&U?_74jS z)&VFk$=S(iadFYmz(C|#HkO*Y<$C$22*5o{Q~BH0R#-^LeAwNpAcMKsI>xbM$8M{t zs9gBi@#@ta;2U#)y1M$HrK(m0QJkNjk5Nb|s$p`|DA%y}cpMI&oW~UtYOuXMJ(+7i z)~BjqAdyk>d6GPbBxzRqva)H)<VJTlkZ<a{?d9b)L9VFKH&+e_4P~Q1*%}}fRn-(T z98m`OIX#6$y7PunmECgm;q&Lkp=k*Td%)lxys{>=-!)5db8|D<O!E2jRUR!3!!v1) zFl{sV)YqrfPoYqd0DX6&78<UV4OS%)@c27N3DMTJRaI39cUN*%l!$*@MZ74vp~GPE zebiE%Pv3-*Ysa&)lneft`VzJMRw!gYVYIi)H#@U0!_wS*rfg&+Sl!hk&jFl=hN}_h zap_okAW}DIvGklxL+%017{oK%y?H{R5c#^^=XrB;Qc{xlI<LPF<F~cBUZiyX^XTZl zXX$^k*=!aI2DRVS*w~2K<2{48-`JRYJ^cZ$UXiscZR4r$NMLw4UPB67ky*PZjFHJ? z(b}4rxTc3P+zUq_U~3zYvUS?nnnTH|3UAtUkYtT;iW!oD%@8gAR4O%bcTBiNVi*i( z@fxXwd^a-E`tl{Rsfc7Czeu=|k<q=%N;owqFE6jQ7J4DU&i?puR1;fWyhO}q4-U4B zi6s(KvJM1-$t<GN>9<9-ex8-CNro!e<VI1;4830+-nzQE+1}jjZgGfin7A)aJvRT- zz<~OGpAYEFIr`Ees)4(kmzw9iXlCZ&wZ+iM$;sT=%qZuA+2qC|Dp2e=%{RREsxJ|X zl&IB8oOt-D&Q>bC`7$9=+e{304V-W93?OLrk2UC2wT06B0Z3K<DMdys{<1>%SJjJX zG$if}98Rj|1}E1|=oUOf9d_gm$Bjke<2jK>O%RATFHVBHE7ow9Q0UD^#b=?6a5w%& zMuy@eM>{(^W8(~rA97SQNG{cnQ;*;rw(Bo+e`)>35s!qFR5F>)sbhh`!Je^+oz`ZW zeKv&SKPZzP8|S|9D?};f!^xTn1N~=8hgYZ%^r{;oDHXjpu-Fp>U8V!thBi;sE{psc z^qgNmlS$T(jfuJV*TM@}SnA=n28TudK><x9!d8cG@U8c4efg~DI}2S4;nNDAPIt-9 zo$XEu3&CH!h+OFGxjHgCYeLJ6J&k*fqCJi%@9{JtAZn2==a~;7yVj*Ttt*phMu*g7 zcqi0*iDaG23&d?17@6mk53UO&q0UI~Hg?<gM97paLt)9lpPwni$i{`jC)%JOuBb&= zdJw81Tiw5L&*g-RiR|Q5CNJLT;A82Bm*j#W2Z6bjm6fICi&Zf(I$F6jWpaLiVVuC% fJplT?slrxNEp18JybVCS^P9ar{XCw!MWp`=b@$dF literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_other@3x.png b/Telegram/Resources/icons/settings/devices/device_web_other@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..b6740fc967660e822ed53b6be221094e5b795521 GIT binary patch literal 2454 zcmbtW`9IX_7ayst*_tAiWenLhM93sFW5&!_hp}%FzDc^4lGKDOk!vbz7$(%18ODt( zBqEW0-zIxpTckV9sATwldVOEt@AnV5Kb+@%p68tBb<TNS@AG<2ioGr7kf^*U2n0HW zwL%d9B>fWMeZYFvLVp$jfd~Qy0b=!?SOqqy09S0FjSWZ>(1k$)$(KR<f4Kl64+sb( zNEHAH0x0n7jJofCRuWb4|N1XSrEFO=2qXf+qAZ+l3T#l^R_r{$?<=DV@k_$fkS-}p zMAB=krw?J0X7<m&P~Y8?)mw5wN$Yhz5CU_cm->3MdY%L+xAHw{F{a<jYrbvn-0k~8 z`TEmsIj(Ws`^rX5Q_$WPJ7!~k@4e!d+`gn(WnbZc02fA;>?^-#8~tm*_sd+1ppeAQ z*X3cROXXOFM{NkZipX>}C+*za+vPVzZO*)oj#%!aydj;>(er7FTpjwhIs0JK-9l&m zL(QkEfbMh!xM=wWPulWd-8q=_6K++PurynW=$f}cZ}3<-d#XNytZ2TwF<EQcQlWpB zzs;L?;a?rt8$48s6`<&(Vk4EVjFm@o7E?q&>d7dczg_3omb%oNFWWX5KQUP5P%_GN z8OHBGnpE40;4$b^?e9yJ(w9TpR|ZQey&9ZlYQV?73zOwwkf7SH6@|M|<18-?zdX+) z3q?8}FVBQd8T;(#&>QWF&dcg~TT5gw?)+R<S?$ySD)KMOBkx6znv!<DCm`D;70=gE zQ6gmKh>F~iXx`drF3g|v_URC>FBi7(b-CYRZ<M<_Oru#p311mdu8G{7X^fkOIvw0E zZ62|d<}R;F+U8zf8=+^RpK^FQ-mfRjeS@H+x#l=!WRmakMgg|EwMAYWE?MH_z6Gq= z-Ca+P(nNKT-OVJ4lefOiSrF~i6R~2ly5iumr8j>@nSVZeZRUhgi0jJE*2W|<vN#2J z-DERp2O8gc=A-iDm-*Hd{RB(>xQm99TdO&6zBn@d>f}pU)}fYS6DFTG-4NyP=vXDC zns-0hTw+AO=)((tp(EJ7SVX_$J^uNHWBpOQUH(u1_vwc$BxOF0SGvIR%<q!YGS=e{ zh&csjzRSHVAgN$x|3sNHT0w_NR&d@Xis@dvHrq5^f+5RYII~ByE5b?V0yh=?)x;h_ zI?0b$^faUvk6*}j)E5Gy3u+8zX6qwp-TfZ5q1Qi+*&s|JmaK^hr(-WksIwFliG+ll ziY5ki2w)uU(OOBGydmy@G-MVIX=UJ(B^9#?KrB^4B?nn5d{84%lUN$_?Q>5j)EwL- zq=HFzdd5ha_~T+Lz*)v;_$_W3KvBjin|z}H9uoKWN#`F(h&Vx%s4gB|EaJVh@Sglk z7(C{2`h1sXz$(CQdir3wnr*>Kcpzu7lQBZIxaSEGIC+pLm8jc-%}}F5r2LVtIZ2Qq zx7L`yKPIl_zTtmnS7UInMZ1sC5W>+!1-J3_xF_xpj_R1jx9LFk>p8?C?oW&g&WW2x zT)*+^VrvqV^;S*G@+s|Gvz*IFp3l|CxbB5c#<|v_EG-v?5ykX+Fwj`R(x`TY!D#!A zcDb;@V#|wTSr3s|V}}l{`l!u9ylBSuw@p2KT;cpN0}D4N&#nTKh)bWC=&p5kwE@EI zNnt4=gXTpme&dI8E$J^~S#Qk)?U^_cYr4_X@%#FSjI;m2X1OywP5sNfVVYg#p{clh z!^kP@$tZB(K7L);Le(FJdX!`J8Kh*S!A0ZQ=QjDTAL1Hk#w&dc(0=l{tR=2cj<p7H zDn<SF4^G>)JF`u90y2|URD2}f_?BnR)C2T^K^(5c8^Qdv@jc<qu>vr^$(L8dQs=Iu zdDm%cT@9D&95BX0w}6BR&h1n9?eg|*2XBIu$|$cuJ6m#i^AlS_8~&QaztyQm1|wB8 zJjT_?2Z<=60#iZ-W{8F|O;y=zD7q9kq#o;+E#J-UL3o)GrHS<;)I9f-15B}yv!PQD zZQIqkoZt{z8mvY$KKzGr3j2byA@Mq{Yb@n4iRm)!-{Ux>?UMU2HU`U<yN7>Av~=<W z?3$WHwCSA{HdANASM~+8U0v7H_^ix=_LJ)=b&UI(PBuhJDlY4ht`wKmyQzBuKJyCY z3FJnEaUIUSjcmNN+CPo>tMhQ0CtlyBXN2p~_@V#hm2p;S|GFz7+I54ls!)ty1h}U_ zr&<Bw7^Rx#$V4i@>&96LoL61lZ(Q}rUy&FDn+k~)_9JN?)QTx)2QzYE>Ma~zmI)(D z*P#BJC*QfhUH3BP4T%qhp<CcL;O+M>W%MBY2@K(`45cy_m|@$dS#iY(sAmHD`&`cp zC@$6X2;``m2_H;MpqP{BRHy0iV$&>CeliU)Fr4`~q2DRe=>%NYp)|E@)9|^F%BXYE zZ(2C(eDnO>T(YCca(_`yjhcvjwX)m6f(S|Z1+irnf}}i6vQa0iBalWI+WgHGvm}@4 z?v^DJc8aH}NwreEm04&OU4?US?(OTKaL=764+;s&w-~9FQw<ziJ62X;90qZ>;XMz7 zb`F$@3mL>IoZH%*^+_^30A|--^FccY49)v$;SoKc(frst+4-(4E#12lpI?Peo4UP< z{u-D*XLT!Ws3GcF=#3QV&d?8kV!aH-jqCkVV;+=(Hz+*1=H%zp<Mkn&nYyr&sbTvb zn;DJ`4Xu3J^<l{&;4j;{NxX+S{$)&pd(%7mrR9w@9go}m58dZp)4hce9plvou@JdS z7_#Ol0ufU1A#2Azc>YV^Ttq4+8&2C^@2DDjyH0J|hikKP^Ra8890?v*uI<|O9(UZY zSGV{~d&*i0zV}{U15cN+Z^(RBplIDv3CT0M>Tcpqe30hVoVA?KSgeUC5AV>`LjUk$ zQN(6`B#0a~%RyK^b!hk*8%Gwh%QVT`5=OI0mEA`Lxf`j2W-$uwKX1GUE!1mfW%M1s z(}058aq0PR20*l4uALCng!tG%)Wo?zVQkkIo&J5#!!YOc?#H4Ow^xz$SH=a^{(Hot z(W>uvw;jJ1Q4%<0mBDidmSv42n_IQxh&Q(czfLG(Z~~Ez7yrpT@t<*I4>PAYZ={Z- Sv2M%!nu)NMwkQ_DH~!xkJAhUI literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_other_large.png b/Telegram/Resources/icons/settings/devices/device_web_other_large.png new file mode 100644 index 0000000000000000000000000000000000000000..200206a6e97336a1e00a35ef4552dc10b5eeb6cd GIT binary patch literal 1081 zcmV-91jhS`P)<h;3K|Lk000e1NJLTq002Ay002A)0ssI2wVqBT00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91JfH&r1ONa40RR91JOBUy0E^%0TmS$Aqe(<TRA>e5n6XPEK@`T#88l#! zbb^hfh(%OT1UoT}h$&Rk*`^TDGJ=&sY_+ry45<`5#U^59{)9AIs3}ws#70ojKoai< z4~Air+pKrHA@OF5@aDa5-hBDy?ab_SbeP8|U=%P47zK<1MggOMQNUIKpKbIT!JeL; znVFe*JRXfkhlYlN!62RA-rg=RFR!kya=G03`T6_%`!_Or3LP68J3c=C`1ts;eE<Lh z>?xVX$p;1o4h{~g)v97EcBN85T^<ZV764u7*f_fvh}qfMmzNizTPl?@nas`24NdIn z>1i1egC<Tw0K}$AN4k5#$}VRrl~O7m9v+6n;bbx?e_(BGP47ul`~ZLfR<l789qFyj zV8Y||^>uD;P6iX1Z*OlqJ3IB^Y2pNbeSIx{y3kQFG>JFUDjMJ4-wzKD3y-O(DIu}6 zw4^f@8>d8piDzVFgzh*=<$0a(0!bYsm0}E!Y&Hvn&(F`^-ric$I0*p&rlRl`y-Dcp zd&!H>*c7Fz3L)|E@PPm1<V10@7=!=-)BPlo`X;UJchhExl`(jk^t_Qs1VTJ3D=WHR zw+RUbu<j=ne$swx{r2lA?Co?q?eqDl!sqAb&CN{(9vvMOn;F;r3V_kq*C);G?d?LL zphBc7m&<;?e`8}Kkw_@NI|jS7T4hBucYoP08NwmgUk-7cE4sS69D@G>76=3w!eK|J z<-tjajOekmvqK5$*_W4>nSGYwqoX6;Z?P8_7Y7FipPruf_V)A;<*~lL&H;-dIGpIA zf3+_zE~tlmK3|*Y?(U8dyL4?x_W%F}Sa*s|T0#s_8>;JZS|0uV{q%o)e5}nPr_hGS zP5}tml0?$V5SCEgrfZ}*I@cXQIR(&E(rWBhFq|rFGlMn~>i<wQt~((%wWnuLwrivt zuCfAfg|yQpuUIU`VzG&diLI?I%aYgK-His5laqhi4n-jV7=SG>mb~%taY76sNY{SZ zV_B6veSY{h?zB8uWw{1eSXkh|w7a{@Ay`c!2d4S?d75l>Y<SAA8*G_uoOn)8PxS*+ zC=^;<UFE=Zc6QcqrPmWWwOi+C>XfnI+V3L(-Y$M_@wL{Z&n<Yj--oRfN!=?1j;1V# zEqaBZc*{is8%=v$)(CIvM6MGMm@l#Y{ry(I78BmQm#im<t#(SWnS>U-fE3>RvVL=| z(afvf|MgN`Np1|IfKk9GU=%P47zK<1KT?6;Ch^1zoyeN`00000NkvXXu0mjfx-#1k literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_other_large@2x.png b/Telegram/Resources/icons/settings/devices/device_web_other_large@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..740152c945e74a7075a5dac1bbff515233a75a73 GIT binary patch literal 2148 zcmb6beLU0acSy++7maQ%?RrVgPsu9MrEOBX%S&R+RxB@}xn5S{B3-j9&2D*}m(A!_ z!b}PCQhDt*=G~<$Z(ZgsMvQse_<h|!e!su(ALlveJm;LxdCqgrdCrsQ<Zw|%`CDZW z2&96(Wa|vzh#gRp2iD5n`BVVOk)1ChLEH|V8Gy0%bw~Tz+k=h)v=T@zApo>%M+Jay z0RVv%^5j4Y0G8X?%ai|~G$K#o|MZTc(wOT!2(+gIZHsV;kP|WA-a25fnIJoVA?dI- zBFTsv=SlT?Hm&FN4LA`Q*l;YpA`bHXr_BByJE@7LBKW<X{85skhJ0K~bEBF~x7tb! zn=x^1f8Fpv?cD`J$}khpVp&eLS$58|SXr!L*JA0^F6MoW5P66Cy%usuKK~15`t&b- z!$0L|Y)47vEW?ESk+)H+qQSdgKR0}56;Aq89w7Quu_hHybw1Lw@$>T=c@rs?hz1iA z6Bid33kwS!vhldeL#Ig=F&oPzc-}}|?6zZ$&E*r#I!9nM5Bz|xb@bX~dyZHv4jOB} z@?kGG<S#6!-Oc)*bnW%c&anG;2=RF48Mc>T-wd@j{;yp9?7aMp2KZH?Y-5FS+;eMX zmSN~@vMpT~_dLT4X|F>=+&RIw0v=5ImuJCIIMK-4&6U}Ykofevm`%x-J{UeuIzL>C zO4yG}&~Y{n)V^U#oq^JO%J1~}MEd(r3mC@(mav&j<~bl-?zP&<$w|`s$5KyK9gD|Q zB-9nFfocnlgW+>q;r1Y$n?1}jc1t>17u#EY^_N@Zh=_>x>{ABk&YsQA(9etg@@Ju% zQc*K>(s*1sVE|(tdPy6aU~gO(F3L8{<b%OrH!fkID%3lL=;_J6{f-wrYU-e?I13>& z4UDSZTiUx*ZH+Q-@a~kfKRah-B^c^_etYGMD_Aj5K|uivh5df`j4RXqRCXS8w(=Ec zz1UAI>@O!zIbh;vR+g8```+A>)wn9PyuiVj2Iea+>|euz>*!xlRqL0|D5piO&OaNz z@Nit%lA56vyAGu0d4(b}IJk6Kl|rFBaU{b7W%?TKsCoLnf->(Ur|q@rZhi3BmTXhL z__LbtEm|>Qf~0B}QK2U`L-4nV3nOoHXkMEV5sgNJ^e1W@2|GmwU#YCDY9w;~BsE6- zpF4dslLmyz=%a()1{NJGtDEV#hZwu4>X&6yV?mtW{?~zNYUd@(8iCv}6=mG@#|<Ff zz3R;Y`20|{H^SWHb%n3bWzHU*UpY#sKcMj*A6L|H{~7Z$)VIt(oTX<Kep%B)x6J7# zV*F)&yq9nbU9Y$yDE~3cXnPEn)>7`<+?%S*GBVQku&LHq)y$GAoZyUR7ZExxQ&HE* zDH1;af#NbyM4A`zjWn&}9K9h(rbbFz_pR_NV*e&TT3Z<k(dhC+^jcRop(-Tb-Nq$q zaqPnE9v-y)DYKqq9mdKPeAPxjDeZ<fXt8qwD;C*tVz1@#50CJbq(Vqn3O;5_nk8qk z+ESG)<(V8@{Q$3dmxe%meX7RTJ2FP;Fg<CjS~Ix6HW?}9Qf3ZzTGX^ZDc-UH9FC6a z@e=uN@KhKkE6Qsqq*h2TRKc^Ir=F_CWN05OZ<9s1)YLRDERKH&NKv}k!8}UTbZHp+ zxhc|VWn1}l4xLWN!O+cE*HnFqpZ<M1MHV7CVGum>W&jKHWQ%Skj8<)-8ky=e@Kvja zgVjA)-uz*t=_C=TA@_=f(YnE=a9=a6DQK{I5y1>vyfl%Fz<f07PH0qzaNOeIg0z3k zA3GT)KXzST;^+D9YJXjOGG7T?Z6JtvdG>~BN6GVaLl<;&AQm`5#{Oh`lrLb5EK1r_ ztUoz66zG9NrrUBR%O3kuc|zv`cc2!zsXixo7?|Oa-gu9OqG)Dv>GYC{mjB7=V=sbm zefg1??LN)#DO|~(G#jf0M;DCID|K?H?vhCi)j+-MnCCkk1Y^z>g?<=mj(Gitzu0lL zexQ+KViNCyt*f$admqWi0xpr*34y+6_tN#WHcupRFBp2DOgVig7##XV$|ZhLbfiz$ z$vMU}3XskCps@L2JJ$f)o9^}nncde4#Ip9IX*-{lK)>CWg632P4f?geuP!ND>i+$r zD4$?f8HnSz5k&s*T3&a*sa}>ADt3D-XgtwcalyYiJL;#HziqTILN4X>^>@)(gU)ni zAo<Z<j%@3aZ+eHE*ykcs@FQrVl-tBRB+PxZ<_4!(kj{4maUj%Po?+hw&h*YiW3;Vm zi=IL8jk@O|oaPEf@QwVECi~y!TupXs7G1;T=;U|JR}RZIR|knCPEc)?3QN5;=VcHB zE`0KQ9F}X`YPYP6PoJ3MKA=zE_Y0dS#cE2~H?Zphq*(xDZ45z>$O^hXcY4Oc4iD6{ zdQOwtjWg`#VXbfavto*=va;~G2AMg)?>rV~rVdO9N$Z%^SKLqi_sWhF$p|y~R5Nz8 zU0f(F*6ztELO0*ne!IRIDzcL{8OxW9L1Qk>dov!eH>yL&cQ=$wJIV}>Bt&^jn$%l< zXtlZSy11c|Wi~1Jt+aO$^ysRLOoHAHVwf|CBgW+Av^(7T)pV%>C5Ym%&s{aSqJYr+ z9i+#0x!}_Qjq4uf*x{P^L%ai6IYx%vNBmTEQfz<KY+CQ`?2Y{SHOGdlJ}mHUPTQeK z#6x<hB0KlyCIsIt?G>{W?yCO#8Trp#{gYW=C;3|>x7tVE`4fQ97aVN4NUym60-)X+ A!2kdN literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/device_web_other_large@3x.png b/Telegram/Resources/icons/settings/devices/device_web_other_large@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6463c3a3430f0485c974cc9e43d79842f65bf183 GIT binary patch literal 3999 zcmc&%hc_I`_t&CEPptZc)q?2JL#&A1=rwwjSW%+aU?oaKx4QKZ(Gp>ay2{ggtloQ# zzKF0`<-6WFzw#e^&$)N*%-lQWo|${+Gf}$Q>eQ61lmr9>)DZA9eY~u`WpW~XSy>LZ z#|uI)eRU84a*%x+-*{$k0)c925eVYz<OGC~jszsPCU|DWGXVkdXF>vEyd=Es`%Lt| z+Un25|5v{?G@eKcARwTnfIL$%^e5cU*4bh*0`#0b0^(Fn5f4MTMmg^ofMV{BzLW{2 z>WUEZYtVbcc<&8WTee}6HJcyP3aC)aB*qGD7~9J9Hpqxa?=tEtRamPEd$n#bCc8PG zsSuLm08=^+Hpa?%VdOSpnCwgi>|&+@G#1AFfat$MsvzsV#1voyb26XSyPbojWE~|r z`~H+~2E|Ihdx6+_uOUmng^0{788@pI*i|Ga4CTJk-rL*Tm&|{I!ye4JwZxrD-i^+F z!!8LOIK!4Lt#-w5hFl)?pZFfFb`g`&Ty9Pt3}i~C?hDxANOD51&p8x)gO1TMH@h9Q zRm?qVxD7*+Ba$5d)hL-o|1fhEPDHpJgF33iF(`c0<wL92(33j9gGJ@*vyN{aN-^%y z&O~qWrad!tmJ8FZW?Mo+a@n4y1hku=+-+dh^70Re6g`4|_wnnUNI&HTaaRy>C9-K) zU!EPA*SggE?wbKqVcVmHqHb4UDci(W@`ML(*OUIFw^rK!^v`f9VA9E!@A7h$x47nZ z>6BGraGFJp2J?uy&ZFuU-w8GULR$UvZlganQEBRij5BEXV~p6QYlO<cM;9|WLdMz? zZb&jokxnjbZ(3YqDV1H``_2Ar%|X*~eBeE<?4G~n*L1b{0k=N)`ajp6CRyU4nt8!! z(i|k0Kg+UpDyowjc)FJ<<Mvm;=0N5#?x4k-gXdN7$pkU2Vq&e^n84>>&=!w1((b-c z;Cz>&GNYneg%!^?jrU_xhG@u!p6Wr%&9&r^VzMl5D?cGAf13mw(OXP04)WuT<&Srd z-fiat(F=f+G@?k!U1qBvTGqHC3@#%8R$zs|qb`7@R`~8@nTGSPVm+FfHZ^%s!f>RN zBh@|rY>n+`lJ0np#$ep3lxbFT(D`qn@}|)&1Fc4z&;dc9oaZ_@bPRFsn9{e3XjkJC zwV}JqReuqc2uh9ST2l+T;nAIzXg*t|^0TsQ3%vu;DqtMwRmSaA*{>u;#5yWKSk)(4 zx#&&4tnmpT&;k(Q5ABV8wQuJ@32X=dU~|lQIfD*WN^x^1y<4oEBm2=IS3t3LuPPqU z!Yd2!E|9z{#j~uljr3Q>NDG!qiGbhhOE4Vz*B52x8QPk(zOB<w9^ne#my+pt@@Byd zQ5VHr-YRklL9SYVGGI4X*vuU3ZaS|miYsqhEM|cPtsl~tQ=3-ck!}~MOChT_$Rkjc z<L0$xPb+ScBAMfXnXfmkg4!R?Un^H<<o^nKI3sB^hl^>PyK=ap7qe*$pw~BvOR$I6 z-_#pz#(oGzB?4mv=bdC>m#EzzDRe;OQF|(Tc=ni*falj^L%}AlmxGxyE2|d%==a*a zAfO*sSBJbf;=byk95$VqDnjI==?4l;Y|}Gvok&aW$Xfey+xhWk8GS<>t@5RNurISm zV}ooQOWkL3xhn(o5~<LqLRz`1L%AV7$Cv@{>9h~)9kj}t-zOk;{XQdE=!eysD)W2f z51S`L&In9d8NZyK&bi?NV!hY8<B;%)?=RUFqKP!?Mcg?Y;j1VnIwG!7aO60%8hP2a z?N{;Us?+n$TyxAkIVHdKnyFoMT}~;$LY*~FHt)#bf#F|KQGZ~Y-y)1B#ULy3@r}Ge zTX~W5BlHTytIz~MLzOg&?>!iJPf+THC>jjR*0tM5J)sX|;QO&kAAV6){?Dmd6P#J= zZUbTZj-I~M6glX9XZQ$3_Hd|BQOmxCGYwp~ys%x<JRU$}Gh8;M|AaJ}39&>5h7nlG zv9ea#u4qDopL4-=l&{7oS=JK7X)`0b$0(V-d)z4=lo@9fz0W&QiO6?~=#EseS!xch zeGadvC^0;erN^fv2j>zUhrGZV;Cuaac}lSO+W~N^e#zODZgV5&^R>65qUJwrBPJpT z@sn2v976*Icrs-@zTm)~_zRj=$yk{Sx0430h!R#Do;4mcS4Dytq9Hc>HPphbD?a<W z-7D#lMLG+~-<;bo`xXhM;u*egT+mY=XTB??{jkv}9t7p{_9plpLfxv~duKtsCYFwU zz4hT7@6Zq972T{jJ2c|)eQu0lnlF7#fO%e>5lS}SK&y@EbZ@5Wh`3WaPz&PxlBRjA zFip=S-~o|go>}faGiUZpz#Pu7iA!vbTi|j0$Rf<lg}v-V?yR%;Y6qBmJzwwT5V2!h z<kXkMD-Co$@E@D#XM>BapX_`uH#4~Vg;ObLy5EaOSn%v(x4h1E0RbDSD;~l=IyZ6B zUbIo@Ot)R2FY6cmP`_kqtzy;ER_}aGUW8a0-VI(ZfmT3~zOF)fKeF@H6O4sub05^Q zWU^JYLwS)@G8xP*d8?7`UDKuck`1gqQH=a0_kWG3bJnnsd2Lj{7OYDVso~M(-QpkF zsENCoOk+ErnBu19z#vacR2z?}C&N8Xa^pPnxDJlT4n8;KE!R#Xo#6QkcW04qv!?jn zc51$EzAm!R32zV6nr`0xdE>tV_r%SU#)@^!Z3@<#ahNKEE84V1o?$A2O|t+12#paM zwdSbtWsPh+T<zfuDYamb?yLxy+wnrRO&?lBQMGk#K;4X|wb$HRFuf8b{q|nF9Z$-H z8r>xuMCPsM_l&nd?jAEuaT?>YPFzN7Un7RldpVRC$QrZ`gnjMfJou{&=z-t+vv(d` zC=@sKq@MF;zaIbUS(C2~G-wfwCPaOTE#P$8mx}KmqFvOf_9h)p@WKzcT|E%)?omIJ zb)Uv-4Rv#k%Zjiss}~uU75>V=fHid2L4fmJgHgpk1#RHR|N5iyKmO=>Z)AwsfHsm) zCo7W{_5j!CcztZv0;MJSf)hK2v`o@ru)2bq7lUdv_kxoGCoBN2VsKJRauFmr%x8SI z-V?>>GFGJZUGZ>FJG+r1RKCK+>UIjWGw<yQU{))g;xuL**)er20m7w<9qW^kfo3uq z!#*pdjBDwujYHicNanD|KF{^mKJ(z?#=GYCVX9KrNSWMsWA)Vr_pqoV2Ek_5p4>mS z&44X^UmfE*uT5yVus7cGP89k5ma=XSKh5E|-Qf<~g;GoFCZ~xKr`HZcId8+(nXZ3j zxitXijq5|he}0^UaA#B=oM4%lMJ*e~KN5bel`oLaAcTOj=ulJ%jl!i(Xw|D|m*OFn z`M8kIGNqUi$2>J%C-(d*$R(puOk93VIzr$dtAgud3KD&eU^px5_QHK)_XZJHfSz;R z{gU7*q@`u~<(J=eF<;Shl&}3m%M}I9NKWIG2fqfGyOsW#t_j$95IT$r@C)ja)_Xs> zQXRChT=jSlXv2SxdD<Vl;HME>1=rDXovvsz?r#et!#{qKLgfzw&NtElkIY`j?M?s4 zcbVRuD)*x}oJrFTabiFPVlh5FDEVu+S;chPlw6dO+kOt)MbpVEhfv(NH;LOFXW5O0 z(?r-njku{-Aq(<lz_!nU4bB>I@`hmnH7q)#vr5-zYY&QC|FW*`Sn#2LeNuO1+g08u zSj_6AX(RUSCr{qnrjn!B{`<2p$SzcC377FNZW3QABxQDxpK{k-;BlqT7CuQxq5?z6 zDtrWxc7h=D$ZlOvZ%|gXUR#h0usT}g?Wd6fz2<E(Q4tgxVDs(TrD5C>5-H6VnO!S% z@B&Dln*8Kt0*3;VeBEHS!ppO+Kf_;VkJfrrwbwS7J?w<otGHc4{f@Z{dXEA(yVVuK z5$>-uJw5?HIiL}#uUz;77xt(CA_GGiAt`&vnf8ellE23!1YB%lhG<L8)zd{^VO%-q zzq4UP&#w}OG1iTK2gnZip6gogl3R?f*Q|V3L50tk9oxkk=c$IrBaEZUg>#&%c!2QQ zZ#*3FwJ9h()-|w;9s`2Yz1F5psni|Nwdy<m7aA1@yMH=L7%oF}(YY@63tg!m=!Y9y z(lM{pi}QdeGhV_te)7{@7zeed9~t<G=HH=W;*sC(L}$qex#*&qNm<5xlm1+uAHSt1 zk7{M$kkJbO5bPrJ8M+@)&h>2kOrHTPVora4fz=tuv&-26rq&PDGKG;3af<~1A3E_j zAvpz`S_rbwuLi85|Bxj2_wr1ZnFEIIZD^jZyA{3CB*@#}P8R0zmWhhwEQIu+_i2mr zszeocdq#0H71hdRi3I^4q$?ewS^FW~-rQG<1lk@Pvl&W#YV_m8aU}<pFBE@oC^GWh ze@eYSQ{}MO6fm8OPYZEuGD)i2B*p$KhL0%ogb1PH@l@<9$O_YC#PZ3wS?#FCmLwhv z!4Sy6(MTGORVg1Cj~&NKqDOx!R5jUc9o4#_zyC$K+<iK9wa*(~jC(&e+7WiR?wTCN zLkv~yofOm8>#$Z@n>SbV^<tk)cZGPkmj5<3i3{_*Lm~E|Pk}nGUw~KHXU7mq_WYV# z*q*0AGZEoTJ!yMGQ*j+uD2@+EDkIH@=Y<;S%}pmjJwZEg<}H@TsXtAlB0rRZdtK2^ zShQ?}-#eEw)K%m_>byVM&BL-?EYL`tQiNIWq=T7Q`C7k3;}LC_q7l@o`uf%1_?%>< z-f<_bzLF$k%9`d$bwNH%q1U999y5QQT?y9+aVKFO_*Da4R2wvO0EURi91Xm`TJD#o z(|Oclube0mzvAPQqkaC!s%aXwB%wsweBr;`O2{{Nb`;B+dqgv__iPP3MU87eR!dn- zAdAL-u(UHJBC@S#0>8_Y{~T955EdVQA|c_q(tfA;b9-Dqc6T28bZj|=iIAS|zd@*E aic3yS3qh7=s}tw9pJRxc_A?~NI`ls>^l;Mv literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/terminate_all.png b/Telegram/Resources/icons/settings/devices/terminate_all.png new file mode 100644 index 0000000000000000000000000000000000000000..ccf538575e787d6296bcd64e9057795e7034c28b GIT binary patch literal 699 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1SIoCSFHz9jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDq2jfl1ZV#WBP} zaBqn1iH(60-#%*^1)hrU>SgS9-855U^2CEWsUd7XPcB=+!`AA$@dghYbGM4-)A9+E zrtJTt`muKUy!VHHKd7y~|K;xCmsa0v?_Rh3zVp1L$f<gRl_65yM?d}CbNS_x%P;T8 zT}<0ty8G_Yq>VRo>i%zbQTk}RFvFzl&fhwF89qj)#s(Lq$hBby&ZY@VKPlQ7uQBWF zvl6S`UIq8%msf^pF-$($vgKk1%bweB4W^w=)tdS%NLz-lxx|#O-I+tO*Uc%zK*B(x z$7vx$UyNS8$fAR-Ceu$}&Dv@r#p_kAa&U8uUjOmrr9q5hT&?|cx&{0gll7*rzMA#< z=bwosUlXjueVD`-p3hRd81Jv|l)rI1=U=7VbhFtHudTVBeaL>UALE7g!w(BmS6$8O zKB_eN<b@+1Dn(Xv|FsDm(5%{fPg}I%Y})R-c|G6#`i~dyRbug28pQH2HS_jcGZC)D zJ>p8YE@hYi1Frx0>M$Ujnqjqb%kM4jHgf%M%c_lL+RQ)Sk<|6^+r0VHzO8Wj^gts( z)~xjNe}@y#KRYhH|GxX^qxsxF|JJSeQni;~>w<8n3&)YiA7_8d7U5xYJl5X2MqS5d z<H?jyPve{IoR(gGX;ZshJhuHXqubuN>#x81s7>A?((4wiAu`j4ZBE&4T>%b{mfIrY zEaf7;tY3w-#ky<k{!c4wxBF8XwN~u^lAx6-8zahPxa1g5X-@UJnIm@kPW!$7{|uo# VAv{u&t3m08!PC{xWt~$(697YIA?5%8 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/terminate_all@2x.png b/Telegram/Resources/icons/settings/devices/terminate_all@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8720d7f23852d3034dd4e7a91734a7341382f8d5 GIT binary patch literal 1253 zcmV<B1RDE^P)<h;3K|Lk000e1NJLTq001xm001xu0ssI2*kEqZ00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NGPf0{UR9Fe^SW75vQ5fFNIVR*8 zl4o8cQHBN{c_m_CWWYf3%qwK-EqP=h<uMTDRVE4}14JGrq{K*}5P8eny`Q`J&pP|; zv-dvdI!E{B4EWanfB(O}z4lsr{cCaD&rf;;)Y5KguZxRIWMpJ&YO1@t``z8$_4Tzh z-;7>sYwPOj>ZhltU(Bzsug~7z-i*Dnsr~)^dwYA=*Vk`uZdO)SHa9mJC0e+%vtwgp z!v>lKXJ=>M-rj1vl|)@#U1kwMNf#Fv4GFlqx=Kq+v$M0qOgcI`;%Tq1uM-m!-QC?e zIXMmv4wUC>fR&XMt7dj~wymvgcXyXJ;w4fy!^6WYlg9@K2Z@P^#8Cn?q9lz*6B-&C z92^`H5<&z#bU{G@ns|Mf(E#jpIXO8YCIK%mugS^Dh=_=#r6n>-1EC<5N|m0TURPHa z5D*}U#3B+{FbrfNj90d`wdLXA;pXNBuUf4h7#P5VJw84XSpuk7AD(z>YHDa`NM8+T zm@zEb5bo*e2@4DJ_Vyke9OOAcK|%5H@rL*UyN;mF&Q4=rxgZ^yr>Cd*i%(Q#WhHsp zS5HP!pcNDt7?_on#gm?&pBoz+@nnCqOrQ+1_=H%AqTTBowR0#oHWq7&XJlq(j*X4! zvn^oJju7@E>Ti5}oUny~RuByj6kw%TSO;~d6-1`*1wVr1#{Xx8{<#s-ei-GXbLZ#h z0uF0=cz6(a^_#xFz9}gwo}Qk2dwUBD3)|b<`W$&d)6>&QNlDmrFD@<w@vLQLW~Qj9 z2!#3hc>xVg?6uglQAePf;Jt5eZ?Ke<lw4k3>hs?%H8nL<VTm(7tuZk%IAGuh@M#nO zkCKCOih_-e4OdrJ@nGeG#N)-RqJmytURZH+b8~E{MVI~B+8PQYR9%<@$H&LK4%Fk( z(b24?4{X*E+TY*L$jD%~OH@cSPPw_cBuIWz6V)_;b6j(Cb4N$V{rx?g;q2^;nvRZ+ zgv~1m$C8GI2GRPzs;Y`ah#vvW@ye)60bu(&JUql9M?iRQvLq4r*O_W-Yiai2Ym|_X zAfDSYg6iw*New_nMTNK;SS=t32M1P7d3m`(H5L%m)YQbPDJv_JQe$pGSoy4)($Z2H zHE?49DMtA*N<J^gwY0QQW`N@2VkJ==wqgnk4-Z!qqkz=f+Dd5w_=-S59LM?i_)uhj zf4`y_IZ}IjJLSX*FDxvSi_rD2uC7via&oe+)94nq3(5vH`T6-qr+^4bfs;9^JEK$l z32=6HX78_{CND3~v{W1p(WD7rWMm{NDvI6wB=X{F*3Zumms%(-C<)ZyZcidg#${`3 z%f2|uYZ^c;h6_`fE@j;)phibWO;f-|p}>fXi<9k6DFLM&4h9%-whx6EQt%~oa&l7I z8N>Uy5MnGk5O*}Vvc!cUrV(53>5x7fxSm4kgS+$P<>ha@KlzmNzyALXL@N`oFaE2r P00000NkvXXu0mjfQ3NL> literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/settings/devices/terminate_all@3x.png b/Telegram/Resources/icons/settings/devices/terminate_all@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d4da46f9fe2c849d20344a591f7a9288171a26bb GIT binary patch literal 1904 zcmV-$2aouPP)<h;3K|Lk000e1NJLTq002k;002k`0ssI2+K(g<00001b5ch_0Itp) z=>Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS>+DSw~RA>e5TWLsDTNKvU(~>Nk z(830-v<W1GM1MFH*o6KnNGX+u9~Dt#7EY-sR#aL<QXm$n4Hkk*QAAPDT$YxanVN}4 zIfWW#X1@2m4qTS&+;cnU-gA8C!o9!N+Iz3z+tb;5t$pq6Y(HBM=%NS4Xzj(!nKKtI zTsUpow4tG)@87>`ZBBFjZf<VJk01Z|@#8<__wwb-J$v?y9XnQY^BNi0ym|AtZ{G-_ z5oA+S)3RmDG%~G@TJ-&vEnCXU%34}l1_lP&+uI*Methxb#Su7+G5!7hfq{YQIH9_h zwQJWlH#Zw|TUJ>|N5_mAGgSA=QiZ#B@Ba1cmn;O*jq>vHEcHW0`FZo^efsoC+S?H| zn4%jtY?wNA>YO=q;^X5n18Mc(;NYoKr_P)?la!Pc6cjXOjOJUkseU+g=n!?z1l`-) z>*3)+btNVynrPqJ+PZ!Fc6)n!Dl&TX_3PJ}b3HvhX=!QU;o&=X?(FRBG}OL*`<A&C z5)xvll_IbeO_(r&32MkroH%jGk|hBF0qoP%-QDf!>1i;0|Nec0$M4_2H<Sy69AFT? zb?cVq)6&(|H90xCqM|}-t2l7r0QLUfy?X{=_4W0%3bagM(2$Ujz+_cp9UUDp!SHo5 zIBJv#4i2U!6#>Iua_7z+%xyY>1uK}|lP6E6n$@9kw0rjKnNcLHaQX7()TAO{>({Rr zWU;6&UAn|H$Hc@?%}VLN!~)~TkB5KEV{tRfOE6G^lD@va*w|RMCPPC*EiqvPSax<c z_6bXrnj!n+$B!E~Zcss73n;6U=D=pnnzeQ7R;J~{hYx%A?p?HKk+ZY2cE<&L_o-bA zH~qh<F3_7cZDLz(K|#T`ZQF245L&jiwJ{NWvK$zOg*y7+!Gq}NXj)<|G@pv@?(Woe z>>`x4BANqZ^AO)<D*{7Z<-lm2Rns<L);4^$5wne$PI|yL6?HNYvfZ$anC!07Nke`J zR_Ye+h*BcBVo6mgEk`Wf+uIw#JRI>F8ynxfd-q2Tih)sgahIv9tCO<P)z!tSs4S`& z%Nh$TLSS)maZyoGGz5bCVM<EM<;#~%9Ay=+vEt(5hK2@M1u7(pjCvzgh-laC+qZ?- z1_-cHEm4k3?THg7&~dwV?V_kq1+;~Qh0mWqpFe-Tlm^8khMC~y<t3#KaSKD-ip4M> zg1!g~3zL#%<%@s+SRrM7eSL9VT(xRdRaF%&7ih)6#42dXzJ2?utE<t$U%!6M&(Eh- zTF^)@-o1O*&(9Ct&Gf4b40{Rk49tdFEP|X44i4<bIAzL|l9CdP-th1+HKcfrQQ=Xe zpFVxMX3d&OlO`d;gVYSe;N#;H5fQ<h(K|5Y7B_C(=<n};^X5(LqDaqYWo1Q1M*jTy zlOjPDlCq%n3k*?iBnI%};L4RN%xUb*MMXulaKVBFlm)Hw8e`hcWASj}?Af#F>FI`0 zu~-k{{^CMfqB~&a<>knInm$`#|5&|xHHCp0OIgr50tPTESFXhN&0G|-85tRK=gy_& z4<A0HtPz@~OK?O$Cdo*n|FtMJHI?OMFfG~91P<zG#N>1r79Nt|tR+rfI1jKw&gyx@ zFaz0Gfkhr~+_-TZh82xiW@aWn%?tqY_ekpFM8e9!TE|qxM_HVeuC1+Iv0}xSFJHu{ ztOMAQBS#J%JjlGSsi^@fyxwBv23GGQcJ$~`_Amji;{pLxc#UO%AtrPU?AWnm`}gl> zPQQ8c2B=INN`&_M1%^!=w>yUF_3PL8i2o54llqUviyykBLc5Vh+7}_gPu&pc%F0T- zH#5^XPM=gG0w!iEvOw(Q$&=IvfqwPsmHDXP45B|&DKhz^{2UDzaO!|S<1sOA$`<sM zogJQyFuho*7HL!>eERfhW*zK{7cay|K_yy$x5bSBOOtAU^5ltPEy@KjFYqFh+7)QT z*i=9z_;c>uIc7k7mQn7xDG8v;&CO*-!Qvc<IEN`oMRj<bgU|vsgW$=I9Xk{$k>424 z>@HlmK+Op>0yr)%F7oxrYI*qZVX>w2=g%XKCre&-qoqri;zhgI7UsYDsNj{8lM^~j zY!81Ez(ifQZr$wJv(eMC{U6Z~v1`PV@oKNMv=nneYz0v9pml_dip=!s)A0sdY*p)$ zM~@z9h)U=ng#2-c)*1=bf9={eyp~jPcqCOJ@`?a5wXG42kQ>ehqkI^!Ly80`AB`Yk znO(hl6@gL7-qo}MeTg^NIXO8<j;VqWIXi59_(CFEq=o0^{4D{&6a3!>yNh}M1VcvV q^XJdF(qsB*0m-J*)&sigfqwyF5E@z27p$)U0000<MNUMnLSTXgjFnIT literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ea9c3e095..ab5fd1b85 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -723,12 +723,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sessions_terminate_all_about" = "Logs out all devices except for this one."; "lng_sessions_incomplete" = "Incomplete login attempts"; "lng_sessions_incomplete_about" = "The devices above have no access to your messages. The code was entered correctly, but no correct password was given."; -"lng_sessions_terminate" = "Terminate"; +"lng_sessions_info" = "Info"; +"lng_sessions_terminate" = "Terminate Session"; "lng_sessions_application" = "Application"; -"lng_sessions_system" = "System Version"; -"lng_sessions_ip" = "IP Address"; +"lng_sessions_system" = "System version"; +"lng_sessions_ip" = "IP address"; "lng_sessions_location" = "Location"; -"lng_sessions_location_about" = "This location estimate is based on the IP address and may not always be accurate."; +"lng_sessions_location_about" = "This location is based only on the IP address and may not always be accurate."; +"lng_sessions_about_apps" = "The official Telegram app is available for Android, iPhone, iPad, Windows, macOS and Linux."; "lng_blocked_list_title" = "Blocked users"; "lng_blocked_list_unknown_phone" = "unknown phone number"; diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index 0f1f457f8..050fc458b 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -17,6 +17,7 @@ namespace Api { namespace { constexpr auto TestApiId = 17349; +constexpr auto SnapApiId = 611335; constexpr auto DesktopApiId = 2040; Authorizations::Entry ParseEntry(const MTPDauthorization &data) { @@ -25,9 +26,11 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { result.hash = data.is_current() ? 0 : data.vhash().v; result.incomplete = data.is_password_pending(); - const auto apiId = data.vapi_id().v; + const auto apiId = result.apiId = data.vapi_id().v; const auto isTest = (apiId == TestApiId); - const auto isDesktop = (apiId == DesktopApiId) || isTest; + const auto isDesktop = (apiId == DesktopApiId) + || (apiId == SnapApiId) + || isTest; const auto appName = isDesktop ? QString("Telegram Desktop%1").arg(isTest ? " (GitHub)" : QString()) @@ -59,6 +62,7 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) { // country = QString::fromUtf8(j.value()->name); //} result.system = qs(data.vsystem_version()); + result.platform = qs(data.vplatform()); result.activeTime = data.vdate_active().v ? data.vdate_active().v : data.vdate_created().v; diff --git a/Telegram/SourceFiles/api/api_authorizations.h b/Telegram/SourceFiles/api/api_authorizations.h index 45092eca2..f789740fc 100644 --- a/Telegram/SourceFiles/api/api_authorizations.h +++ b/Telegram/SourceFiles/api/api_authorizations.h @@ -21,8 +21,9 @@ public: uint64 hash = 0; bool incomplete = false; + int apiId = 0; TimeId activeTime = 0; - QString name, active, info, ip, location, system; + QString name, active, info, ip, location, system, platform; }; using List = std::vector<Entry>; diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 6ee4529a0..5dd5c1894 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -281,10 +281,21 @@ membersAbout: FlatLabel(defaultFlatLabel) { sessionsScroll: boxScroll; sessionsHeight: 350px; -sessionHeight: 70px; -sessionCurrentPadding: margins(0px, 7px, 0px, 4px); -sessionCurrentHeight: 118px; -sessionPadding: margins(22px, 10px, 22px, 0px); +sessionsTerminateAll: SettingsButton(defaultSettingsButton) { + textFg: attentionButtonFg; + textFgOver: attentionButtonFgOver; + font: font(boxFontSize semibold); + height: 20px; + padding: margins(77px, 12px, 22px, 10px); +} +sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }}; +sessionsTerminateAllIconLeft: 30px; +sessionHeight: 84px; +sessionInfoTop: 21px; +sessionLocationTop: 43px; +sessionCurrentSkip: 8px; +sessionSubtitleSkip: 14px; +sessionPadding: margins(77px, 11px, 22px, 0px); sessionNameFont: msgNameFont; sessionNameFg: boxTextFg; sessionWhenFont: msgDateFont; @@ -293,6 +304,8 @@ sessionInfoFont: msgFont; sessionInfoFg: windowSubTextFg; sessionTerminateTop: 9px; sessionTerminateSkip: 22px; +sessionUserpicSize: 42px; +sessionUserpicPosition: point(21px, 10px); sessionNamePadding: margins(0px, 0px, 5px, 0px); sessionTerminate: IconButton { width: 20px; @@ -308,10 +321,6 @@ sessionTerminate: IconButton { color: windowBgOver; } } -sessionTerminateAllButton: LinkButton(boxLinkButton) { - color: attentionButtonFg; - overColor: attentionButtonFg; -} sessionNameStyle: TextStyle(defaultTextStyle) { font: sessionNameFont; } @@ -321,6 +330,19 @@ sessionWhenStyle: TextStyle(defaultTextStyle) { sessionInfoStyle: TextStyle(defaultTextStyle) { font: sessionInfoFont; } +sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }}; +sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }}; +sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }}; +sessionIconLinux: icon{{ "settings/devices/device_linux", historyPeerUserpicFg }}; +sessionIconiPhone: icon{{ "settings/devices/device_phone_ios", historyPeerUserpicFg }}; +sessionIconiPad: icon{{ "settings/devices/device_tablet_ios", historyPeerUserpicFg }}; +sessionIconAndroid: icon{{ "settings/devices/device_phone_android", historyPeerUserpicFg }}; +sessionIconWeb: icon{{ "settings/devices/device_web_other", historyPeerUserpicFg }}; +sessionIconChrome: icon{{ "settings/devices/device_web_chrome", historyPeerUserpicFg }}; +sessionIconEdge: icon{{ "settings/devices/device_web_edge", historyPeerUserpicFg }}; +sessionIconFirefox: icon{{ "settings/devices/device_web_firefox", historyPeerUserpicFg }}; +sessionIconSafari: icon{{ "settings/devices/device_web_safari", historyPeerUserpicFg }}; +sessionIconOther: icon{{ "settings/devices/device_other", historyPeerUserpicFg }}; passcodeHeaderFont: font(19px); passcodeHeaderHeight: 80px; diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 5ede4940d..bab50931f 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/layers/generic_box.h" +#include "lottie/lottie_icon.h" #include "core/application.h" #include "core/core_settings.h" #include "window/window_session_controller.h" @@ -39,6 +40,22 @@ constexpr auto kMaxDeviceModelLength = 32; using EntryData = Api::Authorizations::Entry; +enum class Type { + Windows, + Mac, + Ubuntu, + Linux, + iPhone, + iPad, + Android, + Web, + Chrome, + Edge, + Firefox, + Safari, + Other, +}; + void RenameBox(not_null<Ui::GenericBox*> box) { box->setTitle(tr::lng_settings_rename_device_title()); @@ -133,6 +150,141 @@ void SessionInfoBox( : QString()); } +[[nodiscard]] Type TypeFromEntry(const EntryData &entry) { + using List = std::vector<int>; + const auto platform = entry.platform.toLower(); + const auto device = entry.name.toLower(); + const auto system = entry.system.toLower(); + const auto apiId = entry.apiId; + const auto kDesktop = std::array{ 2040, 17349, 611335 }; + const auto kMac = std::array{ 2834 }; + const auto kAndroid + = std::array{ 5, 6, 24, 1026, 1083, 2458, 2521, 21724 }; + const auto kiOS = std::array{ 1, 7, 10840, 16352 }; + const auto kWeb = std::array{ 2496, 739222, 1025907 }; + + const auto detectBrowser = [&]() -> std::optional<Type> { + if (device.contains("edg/") + || device.contains("edgios/") + || device.contains("edga/")) { + return Type::Edge; + } else if (device.contains("chrome")) { + return Type::Chrome; + } else if (device.contains("safari")) { + return Type::Safari; + } else if (device.contains("firefox")) { + return Type::Firefox; + } + return {}; + }; + const auto detectDesktop = [&]() -> std::optional<Type> { + if (platform.contains("windows") || system.contains("windows")) { + return Type::Windows; + } else if (platform.contains("macos") || system.contains("macos")) { + return Type::Mac; + } else if (platform.contains("ubuntu") + || system.contains("ubuntu") + || platform.contains("unity") + || system.contains("unity")) { + return Type::Ubuntu; + } else if (platform.contains("linux") || system.contains("linux")) { + return Type::Linux; + } + return {}; + }; + + if (ranges::contains(kAndroid, apiId)) { + return Type::Android; + } else if (ranges::contains(kDesktop, apiId)) { + return detectDesktop().value_or(Type::Linux); + } else if (ranges::contains(kMac, apiId)) { + return Type::Mac; + } else if (ranges::contains(kWeb, apiId)) { + return detectBrowser().value_or(Type::Web); + } else if (device.contains("chromebook")) { + return Type::Other; + } else if (const auto browser = detectBrowser()) { + return *browser; + } else if (device.contains("iphone")) { + return Type::iPhone; + } else if (device.contains("ipad")) { + return Type::iPad; + } else if (ranges::contains(kiOS, apiId)) { + return Type::iPhone; + } else if (const auto desktop = detectDesktop()) { + return *desktop; + } else if (platform.contains("android") || system.contains("android")) { + return Type::Android; + } else if (platform.contains("ios") || system.contains("ios")) { + return Type::iPhone; + } + return Type::Other; +} + +[[nodiscard]] style::color ColorForType(Type type) { + switch (type) { + case Type::Windows: + case Type::Mac: + case Type::Other: + return st::historyPeer4UserpicBg; // blue + case Type::Ubuntu: + return st::historyPeer8UserpicBg; // orange + case Type::Linux: + return st::historyPeer5UserpicBg; // purple + case Type::iPhone: + case Type::iPad: + return st::historyPeer7UserpicBg; // sea + case Type::Android: + return st::historyPeer2UserpicBg; // green + case Type::Web: + case Type::Chrome: + case Type::Edge: + case Type::Firefox: + case Type::Safari: + return st::historyPeer6UserpicBg; // pink + } + Unexpected("Type in ColorForType."); +} + +[[nodiscard]] const style::icon &IconForType(Type type) { + switch (type) { + case Type::Windows: return st::sessionIconWindows; + case Type::Mac: return st::sessionIconMac; + case Type::Ubuntu: return st::sessionIconUbuntu; + case Type::Linux: return st::sessionIconLinux; + case Type::iPhone: return st::sessionIconiPhone; + case Type::iPad: return st::sessionIconiPad; + case Type::Android: return st::sessionIconAndroid; + case Type::Web: return st::sessionIconWeb; + case Type::Chrome: return st::sessionIconChrome; + case Type::Edge: return st::sessionIconEdge; + case Type::Firefox: return st::sessionIconFirefox; + case Type::Safari: return st::sessionIconSafari; + case Type::Other: return st::sessionIconOther; + } + Unexpected("Type in IconForType."); +} + +[[nodiscard]] QImage GenerateUserpic(Type type) { + const auto size = st::sessionUserpicSize; + const auto full = size * style::DevicePixelRatio(); + const auto rect = QRect(0, 0, size, size); + + auto result = QImage(full, full, QImage::Format_ARGB32_Premultiplied); + result.fill(Qt::transparent); + result.setDevicePixelRatio(style::DevicePixelRatio()); + + auto p = QPainter(&result); + auto hq = PainterHighQualityEnabler(p); + p.setBrush(ColorForType(type)); + p.setPen(Qt::NoPen); + p.drawEllipse(rect); + IconForType(type).paintInCenter(p, rect); + p.end(); + + return result; +} + } // namespace class SessionsContent : public Ui::RpWidget { @@ -150,20 +302,15 @@ protected: private: struct Entry { Entry() = default; - Entry(const EntryData &entry) - : data(entry) - , incomplete(entry.incomplete) - , activeTime(entry.activeTime) - , name(st::sessionNameStyle, entry.name) - , info(st::sessionInfoStyle, entry.info) - , location(st::sessionInfoStyle, LocationAndDate(entry)) { - }; + explicit Entry(const EntryData &entry); EntryData data; bool incomplete = false; + Type type = Type::Other; TimeId activeTime = 0; Ui::Text::String name, info, location; + QImage userpic; }; struct Full { Entry current; @@ -260,6 +407,17 @@ private: }; +SessionsContent::Entry::Entry(const EntryData &entry) +: data(entry) +, incomplete(entry.incomplete) +, type(TypeFromEntry(entry)) +, activeTime(entry.activeTime) +, name(st::sessionNameStyle, entry.name) +, info(st::sessionInfoStyle, entry.info) +, location(st::sessionInfoStyle, LocationAndDate(entry)) +, userpic(GenerateUserpic(type)) { +}; + SessionsContent::SessionsContent( QWidget*, not_null<Window::SessionController*> controller) @@ -475,17 +633,22 @@ void SessionsContent::Inner::setupContent() { Ui::show(Box(RenameBox), Ui::LayerOption::KeepOther); }); - _current = content->add(object_ptr<List>(content)); + _current = content->add( + object_ptr<List>(content), + style::margins{ 0, 0, 0, st::sessionCurrentSkip }); const auto terminateWrap = content->add( object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( content, object_ptr<Ui::VerticalLayout>(content)))->setDuration(0); const auto terminateInner = terminateWrap->entity(); _terminateAll = terminateInner->add( - object_ptr<Ui::SettingsButton>( + CreateButton( terminateInner, tr::lng_sessions_terminate_all(), - st::terminateSessionsButton)); + st::sessionsTerminateAll, + &st::sessionsTerminateAllIcon, + st::sessionsTerminateAllIconLeft, + &st::attentionButtonFg)); AddSkip(terminateInner); AddDividerText(terminateInner, tr::lng_sessions_terminate_all_about()); @@ -494,7 +657,7 @@ void SessionsContent::Inner::setupContent() { content, object_ptr<Ui::VerticalLayout>(content)))->setDuration(0); const auto incompleteInner = incompleteWrap->entity(); - AddSkip(incompleteInner); + AddSkip(incompleteInner, st::sessionSubtitleSkip); AddSubsectionTitle(incompleteInner, tr::lng_sessions_incomplete()); _incomplete = incompleteInner->add(object_ptr<List>(incompleteInner)); AddSkip(incompleteInner); @@ -505,19 +668,18 @@ void SessionsContent::Inner::setupContent() { content, object_ptr<Ui::VerticalLayout>(content)))->setDuration(0); const auto listInner = listWrap->entity(); - AddSkip(listInner); + AddSkip(listInner, st::sessionSubtitleSkip); AddSubsectionTitle(listInner, tr::lng_sessions_other_header()); _list = listInner->add(object_ptr<List>(listInner)); AddSkip(listInner); + AddDividerText(listInner, tr::lng_sessions_about_apps()); const auto ttlWrap = content->add( object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( content, object_ptr<Ui::VerticalLayout>(content)))->setDuration(0); const auto ttlInner = ttlWrap->entity(); - AddDivider(ttlInner); - AddSkip(ttlInner); - + AddSkip(ttlInner, st::sessionSubtitleSkip); AddSubsectionTitle(ttlInner, tr::lng_settings_terminate_title()); AddButtonWithLabel( @@ -703,19 +865,24 @@ void SessionsContent::List::paintEvent(QPaintEvent *e) { for (auto i = from; i != till; ++i) { const auto &entry = _items[i]; + p.drawImage(st::sessionUserpicPosition, entry.userpic); + const auto nameW = _rowWidth.info; - const auto nameH = entry.name.style()->font->height; const auto infoW = entry.data.hash ? _rowWidth.info : available; - const auto infoH = entry.info.style()->font->height; p.setPen(st::sessionNameFg); entry.name.drawLeftElided(p, x, y, nameW, w); p.setPen(st::boxTextFg); - entry.info.drawLeftElided(p, x, y + nameH, infoW, w); + entry.info.drawLeftElided(p, x, y + st::sessionInfoTop, infoW, w); p.setPen(st::sessionInfoFg); - entry.location.drawLeftElided(p, x, y + nameH + infoH, available, w); + entry.location.drawLeftElided( + p, + x, + y + st::sessionLocationTop, + available, + w); p.translate(0, st::sessionHeight); } diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index a4a00c01d..93269eba3 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -632,10 +632,6 @@ manageDeleteGroupButton: SettingsCountButton(manageGroupTopButtonWithText) { editPeerSkip: 7px; editPeerHistoryVisibilityMargins: margins(15px, 0px, 20px, 16px); -terminateSessionsButton: SettingsButton(infoBlockButton) { - padding: margins(22px, 12px, 22px, 10px); -} - infoEmptyFg: windowSubTextFg; infoEmptyPhoto: icon {{ "info_media_photo_empty", infoEmptyFg }}; infoEmptyVideo: icon {{ "info_media_video_empty", infoEmptyFg }}; @@ -662,8 +658,6 @@ editPeerTopButtonsLayoutSkipCustomBottom: 11px; editPeerHistoryVisibilityTopSkip: 8px; -editPeerDeleteButtonMargins: margins(25px, 11px, 22px, 16px); -editPeerDeleteButton: sessionTerminateAllButton; editPeerPhotoMargins: margins(22px, 16px, 22px, 8px); editPeerTitle: defaultInputField; editPeerTitleMargins: margins(27px, 21px, 22px, 8px); From 92e398e0b7f7cb9f9fbc3c928a8e08f84ad75287 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 26 Nov 2021 20:16:53 +0400 Subject: [PATCH 137/180] Support new skin color modifiers in rlottie. --- .../chat_helpers/stickers_emoji_pack.cpp | 45 +++++-------------- Telegram/ThirdParty/rlottie | 2 +- Telegram/lib_lottie | 2 +- 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index fee492880..b4adb759b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -55,49 +55,24 @@ constexpr auto kRefreshTimeout = 7200 * crl::time(1000); Expects(index >= 1 && index <= 5); static const auto color1 = Lottie::ColorReplacements{ - { - { 0xf77e41U, 0xcb7b55U }, - { 0xffb139U, 0xf6b689U }, - { 0xffd140U, 0xffcda7U }, - { 0xffdf79U, 0xffdfc5U }, - }, - 1, + .modifier = Lottie::SkinModifier::Color1, + .tag = 1, }; static const auto color2 = Lottie::ColorReplacements{ - { - { 0xf77e41U, 0xa45a38U }, - { 0xffb139U, 0xdf986bU }, - { 0xffd140U, 0xedb183U }, - { 0xffdf79U, 0xf4c3a0U }, - }, - 2, + .modifier = Lottie::SkinModifier::Color2, + .tag = 2, }; static const auto color3 = Lottie::ColorReplacements{ - { - { 0xf77e41U, 0x703a17U }, - { 0xffb139U, 0xab673dU }, - { 0xffd140U, 0xc37f4eU }, - { 0xffdf79U, 0xd89667U }, - }, - 3, + .modifier = Lottie::SkinModifier::Color3, + .tag = 3, }; static const auto color4 = Lottie::ColorReplacements{ - { - { 0xf77e41U, 0x4a2409U }, - { 0xffb139U, 0x7d3e0eU }, - { 0xffd140U, 0x965529U }, - { 0xffdf79U, 0xa96337U }, - }, - 4, + .modifier = Lottie::SkinModifier::Color4, + .tag = 4, }; static const auto color5 = Lottie::ColorReplacements{ - { - { 0xf77e41U, 0x200f0aU }, - { 0xffb139U, 0x412924U }, - { 0xffd140U, 0x593d37U }, - { 0xffdf79U, 0x63453fU }, - }, - 5, + .modifier = Lottie::SkinModifier::Color5, + .tag = 5, }; static const auto list = std::array{ &color1, diff --git a/Telegram/ThirdParty/rlottie b/Telegram/ThirdParty/rlottie index cbd43984e..8c69fc20c 160000 --- a/Telegram/ThirdParty/rlottie +++ b/Telegram/ThirdParty/rlottie @@ -1 +1 @@ -Subproject commit cbd43984ebdf783e94c8303c41385bf82aa36d5b +Subproject commit 8c69fc20cf2e150db304311f1233a4b55a8892d7 diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie index c75d91f75..fd4d8576a 160000 --- a/Telegram/lib_lottie +++ b/Telegram/lib_lottie @@ -1 +1 @@ -Subproject commit c75d91f75ef87077f07ea6f7087343274b3eb5ff +Subproject commit fd4d8576adb611780d1dc7dcce4fe0115eabc9c0 From 1d1fa5f98b66efcf5116d0fbae1fe0a709d098a9 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 16:06:41 +0400 Subject: [PATCH 138/180] Improve session details box design. --- Telegram/Resources/qrc/telegram/telegram.qrc | 1 + Telegram/SourceFiles/boxes/boxes.style | 65 ----- Telegram/SourceFiles/boxes/sessions_box.cpp | 256 +++++++++++++++---- Telegram/SourceFiles/settings/settings.style | 90 +++++++ Telegram/lib_lottie | 2 +- 5 files changed, 298 insertions(+), 116 deletions(-) diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc index 13e96d5ea..37013d170 100644 --- a/Telegram/Resources/qrc/telegram/telegram.qrc +++ b/Telegram/Resources/qrc/telegram/telegram.qrc @@ -74,6 +74,7 @@ <file alias="settings/devices/device_desktop_mac.lottie">../../icons/settings/devices/device_desktop_mac.lottie</file> <file alias="settings/devices/device_desktop_win.lottie">../../icons/settings/devices/device_desktop_win.lottie</file> <file alias="settings/devices/device_linux.lottie">../../icons/settings/devices/device_linux.lottie</file> + <file alias="settings/devices/device_linux_ubuntu.lottie">../../icons/settings/devices/device_linux_ubuntu.lottie</file> <file alias="settings/devices/device_phone_android.lottie">../../icons/settings/devices/device_phone_android.lottie</file> <file alias="settings/devices/device_phone_ios.lottie">../../icons/settings/devices/device_phone_ios.lottie</file> <file alias="settings/devices/device_tablet_ios.lottie">../../icons/settings/devices/device_tablet_ios.lottie</file> diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 5dd5c1894..55c010523 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -279,71 +279,6 @@ membersAbout: FlatLabel(defaultFlatLabel) { style: boxLabelStyle; } -sessionsScroll: boxScroll; -sessionsHeight: 350px; -sessionsTerminateAll: SettingsButton(defaultSettingsButton) { - textFg: attentionButtonFg; - textFgOver: attentionButtonFgOver; - font: font(boxFontSize semibold); - height: 20px; - padding: margins(77px, 12px, 22px, 10px); -} -sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }}; -sessionsTerminateAllIconLeft: 30px; -sessionHeight: 84px; -sessionInfoTop: 21px; -sessionLocationTop: 43px; -sessionCurrentSkip: 8px; -sessionSubtitleSkip: 14px; -sessionPadding: margins(77px, 11px, 22px, 0px); -sessionNameFont: msgNameFont; -sessionNameFg: boxTextFg; -sessionWhenFont: msgDateFont; -sessionWhenFg: windowSubTextFg; -sessionInfoFont: msgFont; -sessionInfoFg: windowSubTextFg; -sessionTerminateTop: 9px; -sessionTerminateSkip: 22px; -sessionUserpicSize: 42px; -sessionUserpicPosition: point(21px, 10px); -sessionNamePadding: margins(0px, 0px, 5px, 0px); -sessionTerminate: IconButton { - width: 20px; - height: 20px; - - icon: smallCloseIcon; - iconOver: smallCloseIconOver; - iconPosition: point(5px, 5px); - - rippleAreaPosition: point(0px, 0px); - rippleAreaSize: 20px; - ripple: RippleAnimation(defaultRippleAnimation) { - color: windowBgOver; - } -} -sessionNameStyle: TextStyle(defaultTextStyle) { - font: sessionNameFont; -} -sessionWhenStyle: TextStyle(defaultTextStyle) { - font: sessionWhenFont; -} -sessionInfoStyle: TextStyle(defaultTextStyle) { - font: sessionInfoFont; -} -sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }}; -sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }}; -sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }}; -sessionIconLinux: icon{{ "settings/devices/device_linux", historyPeerUserpicFg }}; -sessionIconiPhone: icon{{ "settings/devices/device_phone_ios", historyPeerUserpicFg }}; -sessionIconiPad: icon{{ "settings/devices/device_tablet_ios", historyPeerUserpicFg }}; -sessionIconAndroid: icon{{ "settings/devices/device_phone_android", historyPeerUserpicFg }}; -sessionIconWeb: icon{{ "settings/devices/device_web_other", historyPeerUserpicFg }}; -sessionIconChrome: icon{{ "settings/devices/device_web_chrome", historyPeerUserpicFg }}; -sessionIconEdge: icon{{ "settings/devices/device_web_edge", historyPeerUserpicFg }}; -sessionIconFirefox: icon{{ "settings/devices/device_web_firefox", historyPeerUserpicFg }}; -sessionIconSafari: icon{{ "settings/devices/device_web_safari", historyPeerUserpicFg }}; -sessionIconOther: icon{{ "settings/devices/device_other", historyPeerUserpicFg }}; - passcodeHeaderFont: font(19px); passcodeHeaderHeight: 80px; passcodeInput: InputField(introPhone) { diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index bab50931f..501fdd428 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/labels.h" #include "ui/widgets/scroll_area.h" #include "ui/wrap/slide_wrap.h" +#include "ui/wrap/padding_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/layers/generic_box.h" #include "lottie/lottie_icon.h" @@ -93,56 +94,6 @@ void RenameBox(not_null<Ui::GenericBox*> box) { box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); } -void SessionInfoBox( - not_null<Ui::GenericBox*> box, - const EntryData &data, - Fn<void(uint64)> terminate) { - box->setTitle(rpl::single(data.name)); - box->setWidth(st::boxWidth); - - const auto skips = style::margins(0, 0, 0, st::settingsSectionSkip); - const auto date = base::unixtime::parse(data.activeTime); - box->addRow( - object_ptr<Ui::FlatLabel>( - box, - rpl::single(langDateTimeFull(date)), - st::boxDividerLabel), - st::boxRowPadding + skips); - - const auto add = [&](rpl::producer<QString> label, QString value) { - if (value.isEmpty()) { - return; - } - Settings::AddSubsectionTitle( - box->verticalLayout(), - std::move(label)); - box->addRow( - object_ptr<Ui::FlatLabel>( - box, - rpl::single(value), - st::boxDividerLabel), - st::boxRowPadding + skips); - }; - add(tr::lng_sessions_application(), data.info); - add(tr::lng_sessions_system(), data.system); - add(tr::lng_sessions_ip(), data.ip); - add(tr::lng_sessions_location(), data.location); - if (!data.location.isEmpty()) { - Settings::AddDividerText( - box->verticalLayout(), - tr::lng_sessions_location_about()); - } - - box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); - box->addLeftButton(tr::lng_sessions_terminate(), [=, hash = data.hash] { - const auto weak = Ui::MakeWeak(box.get()); - terminate(hash); - if (weak) { - box->closeBox(); - } - }, st::attentionBoxButton); -} - [[nodiscard]] QString LocationAndDate(const EntryData &entry) { return (entry.location.isEmpty() ? entry.ip : entry.location) + (entry.hash @@ -265,6 +216,44 @@ void SessionInfoBox( Unexpected("Type in IconForType."); } +[[nodiscard]] const style::icon *IconBigForType(Type type) { + switch (type) { + case Type::Web: return &st::sessionBigIconWeb; + case Type::Other: return &st::sessionBigIconOther; + } + return nullptr; +} + +[[nodiscard]] std::unique_ptr<Lottie::Icon> LottieForType(Type type) { + if (IconBigForType(type)) { + return nullptr; + } + const auto path = [&] { + switch (type) { + case Type::Windows: return "device_desktop_win"; + case Type::Mac: return "device_desktop_mac"; + case Type::Ubuntu: return "device_linux_ubuntu"; + case Type::Linux: return "device_linux"; + case Type::iPhone: return "device_phone_ios"; + case Type::iPad: return "device_tablet_ios"; + case Type::Android: return "device_phone_android"; + case Type::Chrome: return "device_web_chrome"; + case Type::Edge: return "device_web_edge"; + case Type::Firefox: return "device_web_firefox"; + case Type::Safari: return "device_web_safari"; + } + Unexpected("Type in LottieForType."); + }(); + const auto size = st::sessionBigLottieSize; + static const auto kWhite = style::owned_color(Qt::white); + return std::make_unique<Lottie::Icon>(Lottie::IconDescriptor{ + .path = u":/icons/settings/devices/"_q + path + u".lottie"_q, + .color = kWhite.color(), + .sizeOverride = QSize(size, size), + .frame = 1, + }); +} + [[nodiscard]] QImage GenerateUserpic(Type type) { const auto size = st::sessionUserpicSize; const auto full = size * style::DevicePixelRatio(); @@ -285,6 +274,172 @@ void SessionInfoBox( return result; } +[[nodiscard]] not_null<Ui::RpWidget*> GenerateUserpicBig( + not_null<Ui::RpWidget*> parent, + rpl::producer<> shown, + Type type) { + const auto size = st::sessionBigUserpicSize; + const auto full = size * style::DevicePixelRatio(); + const auto rect = QRect(0, 0, size, size); + + const auto result = Ui::CreateChild<Ui::RpWidget>(parent.get()); + result->resize(rect.size()); + struct State { + QImage background; + std::unique_ptr<Lottie::Icon> lottie; + QImage lottieFrame; + QImage colorizedFrame; + }; + const auto state = result->lifetime().make_state<State>(); + state->background = QImage( + full, + full, + QImage::Format_ARGB32_Premultiplied); + state->background.fill(Qt::transparent); + state->background.setDevicePixelRatio(style::DevicePixelRatio()); + state->colorizedFrame = state->lottieFrame = state->background; + + auto p = QPainter(&state->background); + auto hq = PainterHighQualityEnabler(p); + p.setBrush(ColorForType(type)); + p.setPen(Qt::NoPen); + p.drawEllipse(rect); + if (const auto icon = IconBigForType(type)) { + icon->paintInCenter(p, rect); + } + p.end(); + + if ((state->lottie = LottieForType(type))) { + std::move( + shown + ) | rpl::start_with_next([=] { + state->lottie->animate( + [=] { result->update(); }, + 0, + state->lottie->framesCount()); + }, result->lifetime()); + } + + result->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(result); + p.drawImage(QPoint(0, 0), state->background); + if (state->lottie) { + state->lottieFrame.fill(Qt::black); + auto q = QPainter(&state->lottieFrame); + state->lottie->paintInCenter(q, result->rect()); + q.end(); + style::colorizeImage( + state->lottieFrame, + st::historyPeerUserpicFg->c, + &state->colorizedFrame); + p.drawImage(QPoint(0, 0), state->colorizedFrame); + + } + }, result->lifetime()); + + return result; +} + +void SessionInfoBox( + not_null<Ui::GenericBox*> box, + const EntryData &data, + Fn<void(uint64)> terminate) { + box->setWidth(st::boxWideWidth); + + const auto shown = box->lifetime().make_state<rpl::event_stream<>>(); + box->setShowFinishedCallback([=] { + shown->fire({}); + }); + + const auto userpicWrap = box->addRow( + object_ptr<Ui::FixedHeightWidget>(box, st::sessionBigUserpicSize), + st::sessionBigCoverPadding); + const auto big = GenerateUserpicBig( + userpicWrap, + shown->events(), + TypeFromEntry(data)); + userpicWrap->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + big->move((size.width() - big->width()) / 2, 0); + }, userpicWrap->lifetime()); + + const auto nameWrap = box->addRow( + object_ptr<Ui::FixedHeightWidget>( + box, + st::sessionBigName.maxHeight)); + const auto name = Ui::CreateChild<Ui::FlatLabel>( + nameWrap, + rpl::single(data.name), + st::sessionBigName); + nameWrap->widthValue( + ) | rpl::start_with_next([=](int width) { + name->resizeToWidth(width); + name->move((width - name->width()) / 2, 0); + }, name->lifetime()); + + const auto dateWrap = box->addRow( + object_ptr<Ui::FixedHeightWidget>( + box, + st::sessionDateLabel.style.font->height), + style::margins(0, 0, 0, st::sessionDateSkip)); + const auto date = Ui::CreateChild<Ui::FlatLabel>( + dateWrap, + rpl::single( + langDateTimeFull(base::unixtime::parse(data.activeTime))), + st::sessionDateLabel); + rpl::combine( + dateWrap->widthValue(), + date->widthValue() + ) | rpl::start_with_next([=](int outer, int inner) { + date->move((outer - inner) / 2, 0); + }, date->lifetime()); + + using namespace Settings; + const auto container = box->verticalLayout(); + AddDivider(container); + AddSkip(container, st::sessionSubtitleSkip); + AddSubsectionTitle(container, tr::lng_sessions_info()); + + const auto add = [&](rpl::producer<QString> label, QString value) { + if (value.isEmpty()) { + return; + } + container->add( + object_ptr<Ui::FlatLabel>( + container, + rpl::single(value), + st::boxLabel), + st::boxRowPadding + st::sessionValuePadding); + container->add( + object_ptr<Ui::FlatLabel>( + container, + std::move(label), + st::sessionValueLabel), + (st::boxRowPadding + + style::margins{ 0, 0, 0, st::sessionValueSkip })); + }; + add(tr::lng_sessions_application(), data.info); + add(tr::lng_sessions_system(), data.system); + add(tr::lng_sessions_ip(), data.ip); + add(tr::lng_sessions_location(), data.location); + AddSkip(container, st::sessionValueSkip); + if (!data.location.isEmpty()) { + AddDividerText(container, tr::lng_sessions_location_about()); + } + + box->addButton(tr::lng_about_done(), [=] { box->closeBox(); }); + if (const auto hash = data.hash) { + box->addLeftButton(tr::lng_sessions_terminate(), [=] { + const auto weak = Ui::MakeWeak(box.get()); + terminate(hash); + if (weak) { + box->closeBox(); + } + }, st::attentionBoxButton); + } +} + } // namespace class SessionsContent : public Ui::RpWidget { @@ -737,6 +892,7 @@ rpl::producer<uint64> SessionsContent::Inner::terminateOne() const { rpl::producer<EntryData> SessionsContent::Inner::showRequests() const { return rpl::merge( + _current->showRequests(), _incomplete->showRequests(), _list->showRequests()); } diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index e06edc15c..d56433435 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -237,3 +237,93 @@ settingsDeviceName: InputField(defaultInputField) { dictionariesSectionButton: SettingsButton(settingsUpdateToggle) { font: font(14px semibold); } + +sessionsScroll: boxScroll; +sessionsHeight: 350px; +sessionsTerminateAll: SettingsButton(defaultSettingsButton) { + textFg: attentionButtonFg; + textFgOver: attentionButtonFgOver; + font: font(boxFontSize semibold); + height: 20px; + padding: margins(77px, 12px, 22px, 10px); +} +sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }}; +sessionsTerminateAllIconLeft: 30px; +sessionHeight: 84px; +sessionInfoTop: 21px; +sessionLocationTop: 43px; +sessionCurrentSkip: 8px; +sessionSubtitleSkip: 14px; +sessionPadding: margins(77px, 11px, 22px, 0px); +sessionNameFont: msgNameFont; +sessionNameFg: boxTextFg; +sessionWhenFont: msgDateFont; +sessionWhenFg: windowSubTextFg; +sessionInfoFont: msgFont; +sessionInfoFg: windowSubTextFg; +sessionTerminateTop: 9px; +sessionTerminateSkip: 22px; +sessionUserpicSize: 42px; +sessionUserpicPosition: point(21px, 10px); +sessionNamePadding: margins(0px, 0px, 5px, 0px); +sessionTerminate: IconButton { + width: 20px; + height: 20px; + + icon: smallCloseIcon; + iconOver: smallCloseIconOver; + iconPosition: point(5px, 5px); + + rippleAreaPosition: point(0px, 0px); + rippleAreaSize: 20px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: windowBgOver; + } +} +sessionNameStyle: TextStyle(defaultTextStyle) { + font: sessionNameFont; +} +sessionWhenStyle: TextStyle(defaultTextStyle) { + font: sessionWhenFont; +} +sessionInfoStyle: TextStyle(defaultTextStyle) { + font: sessionInfoFont; +} +sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }}; +sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }}; +sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }}; +sessionIconLinux: icon{{ "settings/devices/device_linux", historyPeerUserpicFg }}; +sessionIconiPhone: icon{{ "settings/devices/device_phone_ios", historyPeerUserpicFg }}; +sessionIconiPad: icon{{ "settings/devices/device_tablet_ios", historyPeerUserpicFg }}; +sessionIconAndroid: icon{{ "settings/devices/device_phone_android", historyPeerUserpicFg }}; +sessionIconWeb: icon{{ "settings/devices/device_web_other", historyPeerUserpicFg }}; +sessionIconChrome: icon{{ "settings/devices/device_web_chrome", historyPeerUserpicFg }}; +sessionIconEdge: icon{{ "settings/devices/device_web_edge", historyPeerUserpicFg }}; +sessionIconFirefox: icon{{ "settings/devices/device_web_firefox", historyPeerUserpicFg }}; +sessionIconSafari: icon{{ "settings/devices/device_web_safari", historyPeerUserpicFg }}; +sessionIconOther: icon{{ "settings/devices/device_other", historyPeerUserpicFg }}; +sessionBigUserpicSize: 70px; +sessionBigLottieSize: 52px; +sessionBigIconOther: icon{{ "settings/devices/device_other_large", historyPeerUserpicFg }}; +sessionBigIconWeb: icon{{ "settings/devices/device_web_other_large", historyPeerUserpicFg }}; +sessionBigCoverPadding: margins(0px, 18px, 0px, 7px); +sessionBigName: FlatLabel(defaultFlatLabel) { + textFg: boxTitleFg; + maxHeight: 29px; + style: TextStyle(defaultTextStyle) { + font: font(20px semibold); + linkFont: font(20px semibold); + linkFontOver: font(20px semibold underline); + } + align: align(top); +} +sessionDateLabel: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; + align: align(top); +} +sessionDateSkip: 19px; +sessionValuePadding: margins(0px, 5px, 0px, 2px); +sessionValueLabel: FlatLabel(defaultFlatLabel) { + textFg: windowSubTextFg; +} +sessionValueSkip: 8px; diff --git a/Telegram/lib_lottie b/Telegram/lib_lottie index fd4d8576a..ad7fce76f 160000 --- a/Telegram/lib_lottie +++ b/Telegram/lib_lottie @@ -1 +1 @@ -Subproject commit fd4d8576adb611780d1dc7dcce4fe0115eabc9c0 +Subproject commit ad7fce76f3b403471a296c928bae67cd36b8b2cf From 5309af5d560063b97ad24db91149f5ca682cee1e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 16:26:47 +0400 Subject: [PATCH 139/180] Restrict shared media selection in noforward chats. --- Telegram/SourceFiles/info/info_top_bar.cpp | 20 ++++- Telegram/SourceFiles/info/info_top_bar.h | 2 + .../info/media/info_media_list_widget.cpp | 87 +++++++++++++++---- .../info/media/info_media_list_widget.h | 3 + 4 files changed, 92 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index f7e01b807..79186333e 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -341,8 +341,10 @@ void TopBar::updateSelectionControlsGeometry(int newWidth) { _delete->moveToRight(right, 0, newWidth); right += _delete->width(); } - _forward->moveToRight(right, 0, newWidth); - right += _forward->width(); + if (_canForward) { + _forward->moveToRight(right, 0, newWidth); + right += _forward->width(); + } auto left = 0; _cancelSelection->moveToLeft(left, 0); @@ -411,6 +413,7 @@ void TopBar::setSelectedItems(SelectedItems &&items) { SelectedItems TopBar::takeSelectedItems() { _canDelete = false; + _canForward = false; return std::move(_selectedItems); } @@ -419,11 +422,13 @@ rpl::producer<> TopBar::cancelSelectionRequests() const { } void TopBar::updateSelectionState() { - Expects(_selectionText && _delete); + Expects(_selectionText && _delete && _forward); _canDelete = computeCanDelete(); + _canForward = computeCanForward(); _selectionText->entity()->setValue(generateSelectedText()); _delete->toggle(_canDelete, anim::type::instant); + _forward->toggle(_canForward, anim::type::instant); updateSelectionControlsGeometry(width()); } @@ -437,6 +442,7 @@ void TopBar::createSelectionControls() { return created; }; _canDelete = computeCanDelete(); + _canForward = computeCanForward(); _cancelSelection = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>( this, object_ptr<Ui::IconButton>(this, _st.mediaCancel), @@ -461,8 +467,12 @@ void TopBar::createSelectionControls() { this, object_ptr<Ui::IconButton>(this, _st.mediaForward), st::infoTopBarScale)); + registerToggleControlCallback( + _forward.data(), + [this] { return selectionMode() && _canForward; }); _forward->setDuration(st::infoTopBarDuration); _forward->entity()->addClickHandler([this] { performForward(); }); + _forward->entity()->setVisible(_canForward); _delete = wrap(Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>( this, object_ptr<Ui::IconButton>(this, _st.mediaDelete), @@ -481,6 +491,10 @@ bool TopBar::computeCanDelete() const { return ranges::all_of(_selectedItems.list, &SelectedItem::canDelete); } +bool TopBar::computeCanForward() const { + return ranges::all_of(_selectedItems.list, &SelectedItem::canForward); +} + Ui::StringWithNumbers TopBar::generateSelectedText() const { using Type = Storage::SharedMediaType; const auto phrase = [&] { diff --git a/Telegram/SourceFiles/info/info_top_bar.h b/Telegram/SourceFiles/info/info_top_bar.h index d44a16f63..4d1c7e823 100644 --- a/Telegram/SourceFiles/info/info_top_bar.h +++ b/Telegram/SourceFiles/info/info_top_bar.h @@ -111,6 +111,7 @@ private: bool searchMode() const; Ui::StringWithNumbers generateSelectedText() const; [[nodiscard]] bool computeCanDelete() const; + [[nodiscard]] bool computeCanForward() const; void updateSelectionState(); void createSelectionControls(); void clearSelectionControls(); @@ -153,6 +154,7 @@ private: SelectedItems _selectedItems; bool _canDelete = false; + bool _canForward = false; QPointer<Ui::FadeWrap<Ui::IconButton>> _cancelSelection; QPointer<Ui::FadeWrap<Ui::LabelWithNumbers>> _selectionText; QPointer<Ui::FadeWrap<Ui::IconButton>> _forward; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 7f3d46cee..5940ca79a 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "layout/layout_selection.h" #include "data/data_media_types.h" #include "data/data_photo.h" +#include "data/data_chat.h" +#include "data/data_channel.h" +#include "data/data_peer_values.h" #include "data/data_document.h" #include "data/data_session.h" #include "data/data_file_click_handler.h" @@ -731,9 +734,44 @@ void ListWidget::start() { }, lifetime()); _controller->mediaSourceQueryValue( - ) | rpl::start_with_next([this]{ + ) | rpl::start_with_next([this] { restart(); }, lifetime()); + + setupSelectRestriction(); +} + +void ListWidget::setupSelectRestriction() { + const auto chat = _peer->asChat(); + const auto channel = _peer->asChannel(); + auto noForwards = chat + ? Data::PeerFlagValue(chat, ChatDataFlag::NoForwards) + : Data::PeerFlagValue( + channel, + ChannelDataFlag::NoForwards + ) | rpl::type_erased(); + + auto rights = chat + ? chat->adminRightsValue() + : channel->adminRightsValue(); + auto canDelete = std::move( + rights + ) | rpl::map([=] { + return chat + ? chat->canDeleteMessages() + : channel->canDeleteMessages(); + }); + rpl::combine( + std::move(noForwards), + std::move(canDelete) + ) | rpl::filter([=] { + return hasSelectRestriction() && hasSelectedItems(); + }) | rpl::start_with_next([=] { + clearSelected(); + if (_mouseAction == MouseAction::PrepareSelect) { + mouseActionCancel(); + } + }, lifetime()); } rpl::producer<int> ListWidget::scrollToRequests() const { @@ -1656,18 +1694,20 @@ void ListWidget::showContextMenu( [=] { _contextMenu = nullptr; })); } } - _contextMenu->addAction( - tr::lng_context_select_msg(tr::now), - crl::guard(this, [this, universalId] { - if (hasSelectedText()) { - clearSelected(); - } else if (_selected.size() == MaxSelectedItems) { - return; - } else if (_selected.empty()) { - update(); - } - applyItemSelection(universalId, FullSelection); - })); + if (!hasSelectRestriction()) { + _contextMenu->addAction( + tr::lng_context_select_msg(tr::now), + crl::guard(this, [this, universalId] { + if (hasSelectedText()) { + clearSelected(); + } else if (_selected.size() == MaxSelectedItems) { + return; + } else if (_selected.empty()) { + update(); + } + applyItemSelection(universalId, FullSelection); + })); + } } _contextMenu->setDestroyedCallback(crl::guard( @@ -1739,6 +1779,17 @@ DeleteMessagesBox *ListWidget::deleteItems(MessageIdsList &&items) { return nullptr; } +bool ListWidget::hasSelectRestriction() const { + if (_peer->allowsForwarding()) { + return false; + } else if (const auto chat = _peer->asChat()) { + return !chat->canDeleteMessages(); + } else if (const auto channel = _peer->asChannel()) { + return !channel->canDeleteMessages(); + } + return true; +} + void ListWidget::setActionBoxWeak(QPointer<Ui::RpWidget> box) { if ((_actionBoxWeak = box)) { _actionBoxWeakLifetime = _actionBoxWeak->alive( @@ -2047,7 +2098,7 @@ void ListWidget::updateDragSelection() { if (swapStates) { std::swap(fromState, tillState); } - if (!fromState.itemId || !tillState.itemId) { + if (!fromState.itemId || !tillState.itemId || hasSelectRestriction()) { clearDragSelection(); return; } @@ -2183,12 +2234,12 @@ void ListWidget::mouseActionStart( applyItemSelection(_pressState.itemId, selStatus); _mouseAction = MouseAction::Selecting; repaintItem(pressLayout); - } else { + } else if (!hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; } } } - } else if (!_pressWasInactive) { + } else if (!_pressWasInactive && !hasSelectRestriction()) { _mouseAction = MouseAction::PrepareSelect; // start items select } } @@ -2365,7 +2416,9 @@ void ListWidget::mouseActionFinish( } void ListWidget::applyDragSelection() { - applyDragSelection(_selected); + if (!hasSelectRestriction()) { + applyDragSelection(_selected); + } clearDragSelection(); pushSelectedItems(); } diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.h b/Telegram/SourceFiles/info/media/info_media_list_widget.h index 4c4e39cec..19d39d6ff 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.h @@ -176,6 +176,9 @@ private: int recountHeight(); void refreshHeight(); + void setupSelectRestriction(); + [[nodiscard]] bool hasSelectRestriction() const; + QMargins padding() const; bool isMyItem(not_null<const HistoryItem*> item) const; bool isItemLayout( From 0c3b289af9e393325c3bd0265e6a8b5097b693ec Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 17:36:37 +0400 Subject: [PATCH 140/180] Add floating dates to CalendarBox selecting mode. --- .../SourceFiles/ui/boxes/calendar_box.cpp | 96 +++++++++++++++++++ Telegram/SourceFiles/ui/boxes/calendar_box.h | 3 + 2 files changed, 99 insertions(+) diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp index b6ec11030..eef26902c 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.cpp @@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/effects/ripple_animation.h" +#include "ui/chat/chat_style.h" #include "ui/ui_utility.h" +#include "ui/cached_round_corners.h" #include "lang/lang_keys.h" #include "styles/style_boxes.h" @@ -384,6 +386,78 @@ private: }; +class CalendarBox::FloatingDate final { +public: + FloatingDate(QWidget *parent, not_null<Context*> context); + + [[nodiscard]] rpl::producer<int> widthValue() const; + void move(int x, int y); + + [[nodiscard]] rpl::lifetime &lifetime(); + +private: + void paint(); + + const not_null<Context*> _context; + RpWidget _widget; + CornersPixmaps _corners; + QString _text; + +}; + +CalendarBox::FloatingDate::FloatingDate( + QWidget *parent, + not_null<Context*> context) +: _context(context) +, _widget(parent) +, _corners( + PrepareCornerPixmaps( + HistoryServiceMsgRadius(), + st::roundedBg, + nullptr)) { + _context->monthValue( + ) | rpl::start_with_next([=](QDate month) { + _text = langMonthOfYearFull(month.month(), month.year()); + const auto width = st::msgServiceFont->width(_text); + const auto rect = QRect(0, 0, width, st::msgServiceFont->height); + _widget.resize(rect.marginsAdded(st::msgServicePadding).size()); + _widget.update(); + }, _widget.lifetime()); + + _widget.paintRequest( + ) | rpl::start_with_next([=] { + paint(); + }, _widget.lifetime()); + + _widget.setAttribute(Qt::WA_TransparentForMouseEvents); + _widget.show(); +} + +rpl::producer<int> CalendarBox::FloatingDate::widthValue() const { + return _widget.widthValue(); +} + +void CalendarBox::FloatingDate::move(int x, int y) { + _widget.move(x, y); +} + +rpl::lifetime &CalendarBox::FloatingDate::lifetime() { + return _widget.lifetime(); +} + +void CalendarBox::FloatingDate::paint() { + auto p = Painter(&_widget); + + FillRoundRect(p, _widget.rect(), st::roundedBg, _corners); + + p.setFont(st::msgServiceFont); + p.setPen(st::roundedFg); + p.drawText( + st::msgServicePadding.left(), + st::msgServicePadding.top() + st::msgServiceFont->ascent, + _text); +} + CalendarBox::Inner::Inner( QWidget *parent, not_null<Context*> context, @@ -801,6 +875,28 @@ CalendarBox::CalendarBox(QWidget*, CalendarBoxArgs &&args) }; setupJumps(_previous.data(), &_previousEnabled); setupJumps(_next.data(), &_nextEnabled); + + _context->selectionUpdates( + ) | rpl::start_with_next([=] { + if (!_context->selectionMode()) { + _floatingDate = nullptr; + } else if (!_floatingDate) { + _floatingDate = std::make_unique<FloatingDate>( + this, + _context.get()); + rpl::combine( + _scroll->geometryValue(), + _floatingDate->widthValue() + ) | rpl::start_with_next([=](QRect scroll, int width) { + const auto shift = _st.daysHeight + - _st.padding.top() + - st::calendarDaysFont->height; + _floatingDate->move( + scroll.x() + (scroll.width() - width) / 2, + scroll.y() - shift); + }, _floatingDate->lifetime()); + } + }, lifetime()); } CalendarBox::~CalendarBox() = default; diff --git a/Telegram/SourceFiles/ui/boxes/calendar_box.h b/Telegram/SourceFiles/ui/boxes/calendar_box.h index 43bfa06ba..a1f4d927d 100644 --- a/Telegram/SourceFiles/ui/boxes/calendar_box.h +++ b/Telegram/SourceFiles/ui/boxes/calendar_box.h @@ -89,6 +89,9 @@ private: class Inner; not_null<Inner*> _inner; + class FloatingDate; + std::unique_ptr<FloatingDate> _floatingDate; + class Title; object_ptr<Title> _title; object_ptr<IconButton> _previous; From 287a35d208ebe0748377f4b9796783bd0e4b8b6a Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 26 Nov 2021 07:38:00 +0300 Subject: [PATCH 141/180] Removed MTP* from PrePasswordErrorBox. --- Telegram/SourceFiles/api/api_bot.cpp | 2 +- Telegram/SourceFiles/boxes/passcode_box.cpp | 9 ++++----- Telegram/SourceFiles/boxes/passcode_box.h | 2 +- .../boxes/peers/edit_participant_box.cpp | 13 +++++-------- .../SourceFiles/boxes/peers/edit_participant_box.h | 6 +----- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/api/api_bot.cpp b/Telegram/SourceFiles/api/api_bot.cpp index ff5209a10..5c463ab44 100644 --- a/Telegram/SourceFiles/api/api_bot.cpp +++ b/Telegram/SourceFiles/api/api_bot.cpp @@ -172,7 +172,7 @@ void SendBotCallbackDataWithPassword( api->cloudPassword().reload(); SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty(), [=](const MTP::Error &error) { auto box = PrePasswordErrorBox( - error, + error.type(), session, tr::lng_bots_password_confirm_check_about( tr::now, diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 4dde9db8a..cf39fe39b 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -1438,15 +1438,14 @@ RecoveryEmailValidation ConfirmRecoveryEmail( } [[nodiscard]] object_ptr<Ui::GenericBox> PrePasswordErrorBox( - const MTP::Error &error, + const QString &error, not_null<Main::Session*> session, TextWithEntities &&about) { const auto type = [&] { - const auto &type = error.type(); - if (type == qstr("PASSWORD_MISSING")) { + if (error == u"PASSWORD_MISSING"_q) { return PasswordErrorType::NoPassword; - } else if (type.startsWith(qstr("PASSWORD_TOO_FRESH_")) - || type.startsWith(qstr("SESSION_TOO_FRESH_"))) { + } else if (error.startsWith(u"PASSWORD_TOO_FRESH_"_q) + || error.startsWith(u"SESSION_TOO_FRESH_"_q)) { return PasswordErrorType::Later; } return PasswordErrorType::None; diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h index 40683a17d..4ce6f3934 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.h +++ b/Telegram/SourceFiles/boxes/passcode_box.h @@ -244,6 +244,6 @@ struct RecoveryEmailValidation { const QString &pattern); [[nodiscard]] object_ptr<Ui::GenericBox> PrePasswordErrorBox( - const MTP::Error &error, + const QString &error, not_null<Main::Session*> session, TextWithEntities &&about); diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp index e497a625c..c59f8ed59 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp @@ -451,15 +451,12 @@ void EditAdminBox::transferOwnership() { MTP_inputCheckPasswordEmpty() )).fail([=](const MTP::Error &error) { _checkTransferRequestId = 0; - if (!handleTransferPasswordError(error)) { - const auto box = std::make_shared<QPointer<Ui::ConfirmBox>>(); - const auto callback = crl::guard(this, [=] { + if (!handleTransferPasswordError(error.type())) { + const auto callback = crl::guard(this, [=](Fn<void()> &&close) { transferOwnershipChecked(); - if (*box) { - (*box)->closeBox(); - } + close(); }); - *box = getDelegate()->show(Box<Ui::ConfirmBox>( + getDelegate()->show(Box<Ui::ConfirmBox>( tr::lng_rights_transfer_about( tr::now, lt_group, @@ -473,7 +470,7 @@ void EditAdminBox::transferOwnership() { }).send(); } -bool EditAdminBox::handleTransferPasswordError(const MTP::Error &error) { +bool EditAdminBox::handleTransferPasswordError(const QString &error) { const auto session = &user()->session(); auto about = tr::lng_rights_transfer_check_about( tr::now, diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h index e9327ca85..54f83210f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h @@ -11,10 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" #include "data/data_chat_participant_status.h" -namespace MTP { -class Error; -} // namespace MTP - namespace Ui { class FlatLabel; class LinkButton; @@ -94,7 +90,7 @@ private: not_null<Ui::InputField*> addRankInput(); void transferOwnership(); void transferOwnershipChecked(); - bool handleTransferPasswordError(const MTP::Error &error); + bool handleTransferPasswordError(const QString &error); void requestTransferPassword(not_null<ChannelData*> channel); void sendTransferRequestFrom( QPointer<PasscodeBox> box, From 3647241f71849355dec72ef62214374be9cabbd4 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 26 Nov 2021 23:46:53 +0300 Subject: [PATCH 142/180] Removed unused MTP::Error from callbacks on fail. --- .../SourceFiles/api/api_attached_stickers.cpp | 2 +- .../SourceFiles/api/api_authorizations.cpp | 6 +- .../SourceFiles/api/api_blocked_peers.cpp | 6 +- .../SourceFiles/api/api_chat_participants.cpp | 10 ++-- .../SourceFiles/api/api_cloud_password.cpp | 6 +- .../SourceFiles/api/api_global_privacy.cpp | 4 +- Telegram/SourceFiles/api/api_invite_links.cpp | 15 +++-- Telegram/SourceFiles/api/api_polls.cpp | 6 +- .../SourceFiles/api/api_self_destruct.cpp | 4 +- .../SourceFiles/api/api_sensitive_content.cpp | 4 +- .../api/api_single_message_search.cpp | 6 +- Telegram/SourceFiles/api/api_updates.cpp | 4 +- Telegram/SourceFiles/api/api_user_privacy.cpp | 4 +- Telegram/SourceFiles/api/api_who_read.cpp | 2 +- Telegram/SourceFiles/apiwrap.cpp | 60 +++++++++---------- Telegram/SourceFiles/boxes/passcode_box.cpp | 12 ++-- Telegram/SourceFiles/boxes/passcode_box.h | 1 - .../boxes/peers/add_participants_box.cpp | 4 +- .../boxes/peers/edit_contact_box.cpp | 1 - .../boxes/peers/edit_participants_box.cpp | 8 +-- .../boxes/peers/edit_peer_info_box.cpp | 6 +- .../boxes/peers/edit_peer_invite_link.cpp | 2 +- .../boxes/peers/edit_peer_permissions_box.cpp | 2 +- .../boxes/peers/edit_peer_requests_box.cpp | 2 +- .../SourceFiles/boxes/pin_messages_box.cpp | 2 +- .../SourceFiles/boxes/sticker_set_box.cpp | 6 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 2 +- Telegram/SourceFiles/boxes/url_auth_box.cpp | 8 +-- .../calls/calls_box_controller.cpp | 2 +- Telegram/SourceFiles/calls/calls_call.cpp | 2 +- Telegram/SourceFiles/calls/calls_instance.cpp | 4 +- .../calls/group/calls_choose_join_as.cpp | 2 +- .../calls/group/calls_group_call.cpp | 7 +-- .../chat_helpers/emoji_keywords.cpp | 4 +- .../chat_helpers/gifs_list_widget.cpp | 2 +- .../chat_helpers/send_context_menu.cpp | 2 +- .../chat_helpers/stickers_dice_pack.cpp | 2 +- .../chat_helpers/stickers_emoji_pack.cpp | 2 +- .../chat_helpers/stickers_list_widget.cpp | 5 +- .../SourceFiles/data/data_chat_filters.cpp | 6 +- .../SourceFiles/data/data_cloud_themes.cpp | 6 +- Telegram/SourceFiles/data/data_group_call.cpp | 6 +- Telegram/SourceFiles/data/data_histories.cpp | 31 ++++------ .../SourceFiles/data/data_replies_list.cpp | 6 +- .../data/data_scheduled_messages.cpp | 2 +- .../data/data_search_controller.cpp | 2 +- .../data/data_sponsored_messages.cpp | 4 +- .../data/stickers/data_stickers.cpp | 2 +- .../admin_log/history_admin_log_inner.cpp | 4 +- .../SourceFiles/history/history_message.cpp | 2 +- .../view/controls/history_view_ttl_button.cpp | 2 +- .../view/history_view_context_menu.cpp | 2 - .../polls/info_polls_results_inner_widget.cpp | 2 +- .../inline_bots/inline_results_widget.cpp | 2 +- Telegram/SourceFiles/intro/intro_step.cpp | 2 +- .../SourceFiles/lang/lang_cloud_manager.cpp | 8 +-- Telegram/SourceFiles/main/main_app_config.cpp | 2 +- .../passport/passport_form_controller.cpp | 4 +- .../SourceFiles/settings/settings_main.cpp | 2 +- .../SourceFiles/support/support_helper.cpp | 4 +- .../window/themes/window_theme_editor_box.cpp | 2 +- .../window/window_session_controller.cpp | 8 +-- 62 files changed, 158 insertions(+), 182 deletions(-) diff --git a/Telegram/SourceFiles/api/api_attached_stickers.cpp b/Telegram/SourceFiles/api/api_attached_stickers.cpp index 9a7e9d495..1aa455963 100644 --- a/Telegram/SourceFiles/api/api_attached_stickers.cpp +++ b/Telegram/SourceFiles/api/api_attached_stickers.cpp @@ -59,7 +59,7 @@ void AttachedStickers::request( strongController->show( Box<StickerSetBox>(strongController, setId), Ui::LayerOption::KeepOther); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; if (const auto strongController = weak.get()) { strongController->show( diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index 050fc458b..a360d8e78 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -132,7 +132,7 @@ void Authorizations::reload() { }) | ranges::to<List>; _listChanges.fire({}); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); } @@ -196,7 +196,7 @@ void Authorizations::updateTTL(int days) { MTP_int(days) )).done([=](const MTPBool &result) { _ttlRequestId = 0; - }).fail([=](const MTP::Error &result) { + }).fail([=] { _ttlRequestId = 0; }).send(); _ttlDays = days; @@ -218,7 +218,7 @@ void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) { MTP_bool(disabled) )).done([=](const MTPBool &) { _toggleCallsDisabledRequests.remove(hash); - }).fail([=](const MTP::Error &) { + }).fail([=] { _toggleCallsDisabledRequests.remove(hash); }).send(); _toggleCallsDisabledRequests.emplace(hash, id); diff --git a/Telegram/SourceFiles/api/api_blocked_peers.cpp b/Telegram/SourceFiles/api/api_blocked_peers.cpp index f6edad324..56ec63062 100644 --- a/Telegram/SourceFiles/api/api_blocked_peers.cpp +++ b/Telegram/SourceFiles/api/api_blocked_peers.cpp @@ -90,7 +90,7 @@ void BlockedPeers::block(not_null<PeerData*> peer) { ++_slice->total; _changes.fire_copy(*_slice); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _blockRequests.erase(peer); }).send(); @@ -128,7 +128,7 @@ void BlockedPeers::unblock(not_null<PeerData*> peer, Fn<void()> onDone) { if (onDone) { onDone(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _blockRequests.erase(peer); }).send(); _blockRequests.emplace(peer, requestId); @@ -165,7 +165,7 @@ void BlockedPeers::request(int offset, Fn<void(BlockedPeers::Slice)> onDone) { )).done([=](const MTPcontacts_Blocked &result) { _requestId = 0; onDone(TLToSlice(result, _session->data())); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index 89edb5e32..c1fbc1797 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -387,7 +387,7 @@ void ChatParticipants::requestLast(not_null<ChannelData*> channel) { LOG(("API Error: " "channels.channelParticipantsNotModified received!")); }); - }).fail([this, channel](const MTP::Error &error) { + }).fail([this, channel] { _participantsRequests.remove(channel); }).send(); @@ -416,7 +416,7 @@ void ChatParticipants::requestBots(not_null<ChannelData*> channel) { LOG(("API Error: " "channels.channelParticipantsNotModified received!")); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _botsRequests.remove(channel); }).send(); @@ -445,7 +445,7 @@ void ChatParticipants::requestAdmins(not_null<ChannelData*> channel) { LOG(("API Error: " "channels.channelParticipantsNotModified received!")); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _adminsRequests.remove(channel); }).send(); @@ -638,7 +638,7 @@ void ChatParticipants::kick( _kickRequests.remove(KickRequest(channel, participant)); channel->applyEditBanned(participant, currentRights, rights); - }).fail([this, kick](const MTP::Error &error) { + }).fail([this, kick] { _kickRequests.remove(kick); }).send(); @@ -666,7 +666,7 @@ void ChatParticipants::unblock( } else { channel->updateFullForced(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _kickRequests.remove(kick); }).send(); diff --git a/Telegram/SourceFiles/api/api_cloud_password.cpp b/Telegram/SourceFiles/api/api_cloud_password.cpp index b2f36d0b9..03ca032ad 100644 --- a/Telegram/SourceFiles/api/api_cloud_password.cpp +++ b/Telegram/SourceFiles/api/api_cloud_password.cpp @@ -36,17 +36,17 @@ void CloudPassword::reload() { } _stateChanges.fire_copy(*_state); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); } void CloudPassword::clearUnconfirmedPassword() { _requestId = _api.request(MTPaccount_CancelPasswordEmail( - )).done([=](const MTPBool &result) { + )).done([=] { _requestId = 0; reload(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; reload(); }).send(); diff --git a/Telegram/SourceFiles/api/api_global_privacy.cpp b/Telegram/SourceFiles/api/api_global_privacy.cpp index 3d2c1d462..c940dbe86 100644 --- a/Telegram/SourceFiles/api/api_global_privacy.cpp +++ b/Telegram/SourceFiles/api/api_global_privacy.cpp @@ -33,7 +33,7 @@ void GlobalPrivacy::reload(Fn<void()> callback) { for (const auto &callback : base::take(_callbacks)) { callback(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; for (const auto &callback : base::take(_callbacks)) { callback(); @@ -86,7 +86,7 @@ void GlobalPrivacy::update(bool archiveAndMute) { )).done([=](const MTPGlobalPrivacySettings &result) { _requestId = 0; apply(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); _archiveAndMute = archiveAndMute; diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index d53c3857e..8206c6bb4 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -128,7 +128,7 @@ void InviteLinks::performCreate( callback(link); } } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _createCallbacks.erase(peer); }).send(); } @@ -313,7 +313,7 @@ void InviteLinks::performEdit( prepend(peer, admin, data.vnew_invite()); } }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _editCallbacks.erase(key); }).send(); } @@ -375,7 +375,7 @@ void InviteLinks::destroy( .admin = admin, .was = key.link, }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _deleteCallbacks.erase(key); }).send(); } @@ -405,7 +405,6 @@ void InviteLinks::destroyAllRevoked( } } _allRevokedDestroyed.fire({ peer, admin }); - }).fail([=](const MTP::Error &error) { }).send(); } @@ -446,7 +445,7 @@ void InviteLinks::requestMyLinks(not_null<PeerData*> peer) { i->second.count = std::max(slice.count, int(existing.size())); } notify(peer); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _firstSliceRequests.remove(peer); }).send(); _firstSliceRequests.emplace(peer, requestId); @@ -507,7 +506,7 @@ void InviteLinks::processRequest( done(); } } - }).fail([=](const MTP::Error &error) { + }).fail([=] { if (const auto callbacks = _processRequests.take({ peer, user })) { if (const auto &fail = callbacks->fail) { fail(); @@ -608,7 +607,7 @@ void InviteLinks::requestJoinedFirstSlice(LinkKey key) { _firstJoinedRequests.remove(key); _firstJoined[key] = ParseJoinedByLinkSlice(key.peer, result); _joinedFirstSliceLoaded.fire_copy(key); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _firstJoinedRequests.remove(key); }).send(); _firstJoinedRequests.emplace(key, requestId); @@ -771,7 +770,7 @@ void InviteLinks::requestMoreLinks( MTP_int(kPerPage) )).done([=](const MTPmessages_ExportedChatInvites &result) { done(parseSlice(peer, result)); - }).fail([=](const MTP::Error &error) { + }).fail([=] { done(Links()); }).send(); } diff --git a/Telegram/SourceFiles/api/api_polls.cpp b/Telegram/SourceFiles/api/api_polls.cpp index 80c3c55ae..7f4924a41 100644 --- a/Telegram/SourceFiles/api/api_polls.cpp +++ b/Telegram/SourceFiles/api/api_polls.cpp @@ -150,7 +150,7 @@ void Polls::sendVotes( _pollVotesRequestIds.erase(itemId); hideSending(); _session->updates().applyUpdates(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _pollVotesRequestIds.erase(itemId); hideSending(); }).send(); @@ -179,7 +179,7 @@ void Polls::close(not_null<HistoryItem*> item) { )).done([=](const MTPUpdates &result) { _pollCloseRequestIds.erase(itemId); _session->updates().applyUpdates(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _pollCloseRequestIds.erase(itemId); }).send(); _pollCloseRequestIds.emplace(itemId, requestId); @@ -196,7 +196,7 @@ void Polls::reloadResults(not_null<HistoryItem*> item) { )).done([=](const MTPUpdates &result) { _pollReloadRequestIds.erase(itemId); _session->updates().applyUpdates(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _pollReloadRequestIds.erase(itemId); }).send(); _pollReloadRequestIds.emplace(itemId, requestId); diff --git a/Telegram/SourceFiles/api/api_self_destruct.cpp b/Telegram/SourceFiles/api/api_self_destruct.cpp index ea7de681b..6247f3037 100644 --- a/Telegram/SourceFiles/api/api_self_destruct.cpp +++ b/Telegram/SourceFiles/api/api_self_destruct.cpp @@ -25,7 +25,7 @@ void SelfDestruct::reload() { result.match([&](const MTPDaccountDaysTTL &data) { _days = data.vdays().v; }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); } @@ -42,7 +42,7 @@ void SelfDestruct::update(int days) { MTP_accountDaysTTL(MTP_int(days)) )).done([=](const MTPBool &result) { _requestId = 0; - }).fail([=](const MTP::Error &result) { + }).fail([=] { _requestId = 0; }).send(); _days = days; diff --git a/Telegram/SourceFiles/api/api_sensitive_content.cpp b/Telegram/SourceFiles/api/api_sensitive_content.cpp index a18967368..d12084dbe 100644 --- a/Telegram/SourceFiles/api/api_sensitive_content.cpp +++ b/Telegram/SourceFiles/api/api_sensitive_content.cpp @@ -36,7 +36,7 @@ void SensitiveContent::reload() { _enabled = data.is_sensitive_enabled(); _canChange = data.is_sensitive_can_change(); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); } @@ -63,7 +63,7 @@ void SensitiveContent::update(bool enabled) { MTP_flags(enabled ? Flag::f_sensitive_enabled : Flag(0)) )).done([=](const MTPBool &result) { _requestId = 0; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); _enabled = enabled; diff --git a/Telegram/SourceFiles/api/api_single_message_search.cpp b/Telegram/SourceFiles/api/api_single_message_search.cpp index 781a1740a..4b374f3bb 100644 --- a/Telegram/SourceFiles/api/api_single_message_search.cpp +++ b/Telegram/SourceFiles/api/api_single_message_search.cpp @@ -117,7 +117,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel( } else { fail(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { fail(); }).send(); @@ -154,7 +154,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupById( fail(); } }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { fail(); }).send(); @@ -198,7 +198,7 @@ std::optional<HistoryItem*> SingleMessageSearch::performLookupByUsername( fail(); } }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { fail(); }).send(); diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index ffefc85de..aa764490b 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -769,7 +769,7 @@ void Updates::channelRangeDifferenceSend( )).done([=](const MTPupdates_ChannelDifference &result) { _rangeDifferenceRequests.remove(channel); channelRangeDifferenceDone(channel, range, result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _rangeDifferenceRequests.remove(channel); }).send(); _rangeDifferenceRequests.emplace(channel, requestId); @@ -921,7 +921,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) { MTP_bool(!isOnline) )).done([=](const MTPBool &result) { Core::App().quitPreventFinished(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { Core::App().quitPreventFinished(); }).send(); } diff --git a/Telegram/SourceFiles/api/api_user_privacy.cpp b/Telegram/SourceFiles/api/api_user_privacy.cpp index 6baf18129..7a5f01de7 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.cpp +++ b/Telegram/SourceFiles/api/api_user_privacy.cpp @@ -241,7 +241,7 @@ void UserPrivacy::save( _privacySaveRequests.remove(keyTypeId); apply(keyTypeId, data.vrules(), true); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _privacySaveRequests.remove(keyTypeId); }).send(); @@ -277,7 +277,7 @@ void UserPrivacy::reload(Key key) { _session->data().processChats(data.vchats()); pushPrivacy(key, data.vrules()); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _privacyRequestIds.erase(key); }).send(); _privacyRequestIds.emplace(key, requestId); diff --git a/Telegram/SourceFiles/api/api_who_read.cpp b/Telegram/SourceFiles/api/api_who_read.cpp index d7fcbb8ff..40f659c95 100644 --- a/Telegram/SourceFiles/api/api_who_read.cpp +++ b/Telegram/SourceFiles/api/api_who_read.cpp @@ -164,7 +164,7 @@ struct State { peers.push_back(UserId(id)); } entry.list = std::move(peers); - }).fail([=](const MTP::Error &error) { + }).fail([=] { auto &entry = context->cache(item); entry.requestId = 0; if (ListUnknown(entry.list.current(), item)) { diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d5936b946..b7567a5a4 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -226,7 +226,7 @@ void ApiWrap::refreshTopPromotion() { )).done([=](const MTPhelp_PromoData &result) { _topPromotionRequestId = 0; topPromotionDone(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _topPromotionRequestId = 0; const auto now = base::unixtime::now(); const auto next = _topPromotionNextRequestTime = now @@ -276,7 +276,7 @@ void ApiWrap::requestDeepLinkInfo( if (result.type() == mtpc_help_deepLinkInfo) { callback(result.c_help_deepLinkInfo()); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _deepLinkInfoRequestId = 0; }).send(); } @@ -323,7 +323,7 @@ void ApiWrap::requestTermsUpdate() { } break; default: Unexpected("Type in requestTermsUpdate()."); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _termsUpdateRequestId = 0; _termsUpdateSendAt = crl::now() + kTermsUpdateTimeoutMin; requestTermsUpdate(); @@ -455,7 +455,7 @@ void ApiWrap::toggleHistoryArchived( if (isPinned) { _session->data().notifyPinnedDialogsOrderUpdated(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _historyArchivedRequests.remove(history); }).send(); _historyArchivedRequests.emplace(history, requestId, callback); @@ -736,7 +736,7 @@ void ApiWrap::requestContacts() { } } _session->data().contactsLoaded() = true; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _contactsRequestId = 0; }).send(); } @@ -810,7 +810,7 @@ void ApiWrap::requestMoreDialogs(Data::Folder *folder) { } requestMoreDialogsIfNeeded(); _session->data().chatsListChanged(folder); - }).fail([=](const MTP::Error &error) { + }).fail([=] { dialogsLoadState(folder)->requestId = 0; }).send(); @@ -955,7 +955,7 @@ void ApiWrap::requestPinnedDialogs(Data::Folder *folder) { _session->data().chatsListChanged(folder); _session->data().notifyPinnedDialogsOrderUpdated(); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finalize(); }).send(); } @@ -1138,7 +1138,7 @@ void ApiWrap::requestPeer(not_null<PeerData*> peer) { } const auto requestId = [&] { - const auto failHandler = [=](const MTP::Error &error) { + const auto failHandler = [=] { _peerRequests.remove(peer); }; const auto chatHandler = [=](const MTPmessages_Chats &result) { @@ -1183,7 +1183,7 @@ void ApiWrap::requestPeerSettings(not_null<PeerData*> peer) { peer->setSettings(data.vsettings()); _requestedPeerSettings.erase(peer); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestedPeerSettings.erase(peer); }).send(); } @@ -1437,7 +1437,7 @@ void ApiWrap::requestStickerSets() { MTP_int(0) // hash )).done([=, setId = i.key()](const MTPmessages_StickerSet &result) { gotStickerSet(setId, result); - }).fail([=, setId = i.key()](const MTP::Error &error) { + }).fail([=, setId = i.key()] { _stickerSetRequests.remove(setId); }).afterDelay(waitMs).send(); } @@ -1480,7 +1480,7 @@ void ApiWrap::saveStickerSets( MTP_vector<MTPlong>(mtpOrder) )).done([=](const MTPBool &result) { reorderRequestId() = 0; - }).fail([=](const MTP::Error &error) { + }).fail([=] { reorderRequestId() = 0; if (setsMasks) { _session->data().stickers().setLastMasksUpdate(0); @@ -1553,7 +1553,7 @@ void ApiWrap::saveStickerSets( MTP_flags(flags) )).done([=](const MTPBool &result) { finish(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finish(); }).send(); continue; @@ -1755,7 +1755,7 @@ void ApiWrap::leaveChannel(not_null<ChannelData*> channel) { )).done([=](const MTPUpdates &result) { _channelAmInRequests.remove(channel); applyUpdates(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _channelAmInRequests.remove(channel); }).send(); @@ -1797,7 +1797,7 @@ void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) { )).done([=](const MTPPeerNotifySettings &result) { applyNotifySettings(peer, result); _notifySettingRequests.erase(key); - }).fail([=](const MTP::Error &error) { + }).fail([=] { applyNotifySettings( peer, MTP_peerNotifySettings( @@ -1879,7 +1879,7 @@ void ApiWrap::updatePrivacyLastSeens() { } } } - }).fail([this](const MTP::Error &error) { + }).fail([this] { _contactsStatusesRequestId = 0; }).send(); } @@ -1913,7 +1913,7 @@ void ApiWrap::deleteConversation(not_null<PeerData*> peer, bool revoke) { )).done([=](const MTPUpdates &result) { applyUpdates(result); deleteHistory(peer, false, revoke); - }).fail([=](const MTP::Error &error) { + }).fail([=] { deleteHistory(peer, false, revoke); }).send(); } else { @@ -2337,7 +2337,7 @@ void ApiWrap::requestFileReference( for (auto &handler : handlers) { handler(parsed); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { const auto i = _fileReferenceHandlers.find(origin); Assert(i != end(_fileReferenceHandlers)); auto handlers = std::move(i->second); @@ -2590,7 +2590,7 @@ void ApiWrap::requestStickers(TimeId now) { }; _stickersUpdateRequest = request(MTPmessages_GetAllStickers( MTP_long(Api::CountStickersHash(_session, true)) - )).done(done).fail([=](const MTP::Error &error) { + )).done(done).fail([=] { LOG(("App Fail: Failed to get stickers!")); done(MTP_messages_allStickersNotModified()); }).send(); @@ -2614,7 +2614,7 @@ void ApiWrap::requestMasks(TimeId now) { }; _masksUpdateRequest = request(MTPmessages_GetMaskStickers( MTP_long(Api::CountMasksHash(_session, true)) - )).done(done).fail([=](const MTP::Error &error) { + )).done(done).fail([=] { LOG(("App Fail: Failed to get masks!")); done(MTP_messages_allStickersNotModified()); }).send(); @@ -2674,7 +2674,7 @@ void ApiWrap::requestRecentStickersWithHash(uint64 hash, bool attached) { } return; default: Unexpected("Type in ApiWrap::recentStickersDone()"); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { finish(); LOG(("App Fail: Failed to get recent stickers!")); @@ -2705,7 +2705,7 @@ void ApiWrap::requestFavedStickers(TimeId now) { } return; default: Unexpected("Type in ApiWrap::favedStickersDone()"); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _session->data().stickers().setLastFavedUpdate(crl::now()); _favedStickersUpdateRequest = 0; @@ -2735,7 +2735,7 @@ void ApiWrap::requestFeaturedStickers(TimeId now) { } return; default: Unexpected("Type in ApiWrap::featuredStickersDone()"); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _session->data().stickers().setLastFeaturedUpdate(crl::now()); _featuredStickersUpdateRequest = 0; @@ -2764,7 +2764,7 @@ void ApiWrap::requestSavedGifs(TimeId now) { } return; default: Unexpected("Type in ApiWrap::savedGifsDone()"); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _session->data().stickers().setLastSavedGifsUpdate(crl::now()); _savedGifsUpdateRequest = 0; @@ -2923,7 +2923,7 @@ void ApiWrap::preloadEnoughUnreadMentions(not_null<History*> history) { auto requestId = request(MTPmessages_GetUnreadMentions(history->peer->input, MTP_int(offsetId), MTP_int(addOffset), MTP_int(limit), MTP_int(maxId), MTP_int(minId))).done([this, history](const MTPmessages_Messages &result) { _unreadMentionsRequests.remove(history); history->addUnreadMentionsSlice(result); - }).fail([this, history](const MTP::Error &error) { + }).fail([this, history] { _unreadMentionsRequests.remove(history); }).send(); _unreadMentionsRequests.emplace(history, requestId); @@ -2982,7 +2982,7 @@ void ApiWrap::requestSharedMedia( _sharedMediaRequests.remove(key); sharedMediaDone(peer, type, messageId, slice, result); finish(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _sharedMediaRequests.remove(key); finish(); }).send(); @@ -3031,7 +3031,7 @@ void ApiWrap::requestUserPhotos( )).done([this, user, afterId](const MTPphotos_Photos &result) { _userPhotosRequests.remove(user); userPhotosDone(user, afterId, result); - }).fail([this, user](const MTP::Error &error) { + }).fail([this, user] { _userPhotosRequests.remove(user); }).send(); _userPhotosRequests.emplace(user, requestId); @@ -3823,7 +3823,7 @@ void ApiWrap::uploadAlbumMedia( sendAlbumWithUploaded(item, groupId, media); } break; } - }).fail([=](const MTP::Error &error) { + }).fail([=] { failed(); }).send(); } @@ -4034,7 +4034,7 @@ void ApiWrap::reloadContactSignupSilent() { const auto silent = mtpIsTrue(result); _contactSignupSilent = silent; _contactSignupSilentChanges.fire_copy(silent); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _contactSignupSilentRequestId = 0; }).send(); _contactSignupSilentRequestId = requestId; @@ -4060,7 +4060,7 @@ void ApiWrap::saveContactSignupSilent(bool silent) { _contactSignupSilentRequestId = 0; _contactSignupSilent = silent; _contactSignupSilentChanges.fire_copy(silent); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _contactSignupSilentRequestId = 0; }).send(); _contactSignupSilentRequestId = requestId; @@ -4085,7 +4085,7 @@ void ApiWrap::saveSelfBio(const QString &text) { _session->data().processUser(result); _session->user()->setAbout(_bio.requestedText); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _bio.requestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index cf39fe39b..5d8ab1f28 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -478,10 +478,6 @@ void PasscodeBox::closeReplacedBy() { } } -void PasscodeBox::setPasswordFail(const MTP::Error &error) { - setPasswordFail(error.type()); -} - void PasscodeBox::setPasswordFail(const QString &type) { if (MTP::IsFloodError(type)) { closeReplacedBy(); @@ -533,7 +529,7 @@ void PasscodeBox::setPasswordFail( validateEmail(email, codeLength, newPasswordBytes); } else { - setPasswordFail(error); + setPasswordFail(error.type()); } } @@ -582,7 +578,7 @@ void PasscodeBox::validateEmail( )).done([=](const MTPBool &result) { _setRequest = 0; resent->fire(tr::lng_cloud_password_resent(tr::now)); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _setRequest = 0; errors->fire(Lang::Hard::ServerError()); }).send(); @@ -942,7 +938,7 @@ void PasscodeBox::changeCloudPassword( }); } }).fail([=](const MTP::Error &error) { - setPasswordFail(error); + setPasswordFail(error.type()); }).handleFloodErrors().send(); } @@ -1419,7 +1415,7 @@ RecoveryEmailValidation ConfirmRecoveryEmail( )).done([=](const MTPBool &result) { *requestId = 0; resent->fire(tr::lng_cloud_password_resent(tr::now)); - }).fail([=](const MTP::Error &error) { + }).fail([=] { *requestId = 0; errors->fire(Lang::Hard::ServerError()); }).send(); diff --git a/Telegram/SourceFiles/boxes/passcode_box.h b/Telegram/SourceFiles/boxes/passcode_box.h index 4ce6f3934..4d731d749 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.h +++ b/Telegram/SourceFiles/boxes/passcode_box.h @@ -98,7 +98,6 @@ private: void recoverPasswordDone( const QByteArray &newPasswordBytes, const MTPauth_Authorization &result); - void setPasswordFail(const MTP::Error &error); void setPasswordFail(const QString &type); void setPasswordFail( const QByteArray &newPasswordBytes, diff --git a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp index 01dbff4af..1c70ee885 100644 --- a/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/add_participants_box.cpp @@ -490,7 +490,7 @@ void AddSpecialBoxController::loadMoreRows() { setDescriptionText(tr::lng_blocked_list_not_found(tr::now)); } delegate()->peerListRefreshRows(); - }).fail([this](const MTP::Error &error) { + }).fail([this] { _loadRequestId = 0; }).send(); } @@ -530,7 +530,7 @@ bool AddSpecialBoxController::checkInfoLoaded( Api::ChatParticipant(data.vparticipant(), channel)); }); callback(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _additional.setExternal(participant); callback(); }).send(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp index b7c473362..96fc581b4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_contact_box.cpp @@ -73,7 +73,6 @@ void SendRequest( lt_user, first)); } - }).fail([=](const MTP::Error &error) { }).send(); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index bc9b9508e..ad09dde7d 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -61,7 +61,7 @@ void RemoveAdmin( if (onDone) { onDone(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { if (onFail) { onFail(); } @@ -168,7 +168,7 @@ void SaveChannelRestriction( if (onDone) { onDone(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { if (onFail) { onFail(); } @@ -189,7 +189,7 @@ void SaveChatParticipantKick( if (onDone) { onDone(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { if (onFail) { onFail(); } @@ -1418,7 +1418,7 @@ void ParticipantsBoxController::loadMoreRows() { _onlineSorter->sort(); } delegate()->peerListRefreshRows(); - }).fail([this](const MTP::Error &error) { + }).fail([this] { _loadRequestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 98f9b4e31..84fae3ba8 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -242,7 +242,7 @@ void ShowEditPermissions( const auto api = &peer->session().api(); api->migrateChat(chat, [=](not_null<ChannelData*> channel) { save(channel, result); - }, [=](const MTP::Error &error) { + }, [=](const MTP::Error &) { *saving = false; }); }, box->lifetime()); @@ -685,7 +685,7 @@ void Controller::showEditLinkedChatBox() { std::move(chats), callback), Ui::LayerOption::KeepOther); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _linkedChatsRequestId = 0; }).send(); } @@ -1361,7 +1361,7 @@ void Controller::saveLinkedChat() { )).done([=](const MTPBool &result) { channel->setLinkedChat(*_savingData.linkedChat); continueSave(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { cancelSave(); }).send(); } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 61b6af100..33b648a49 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -689,7 +689,7 @@ void Controller::loadMoreRows() { auto slice = Api::ParseJoinedByLinkSlice(_peer, result); _allLoaded = slice.users.empty(); appendSlice(slice); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; _allLoaded = true; }).send(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index a83f24beb..6910a43a6 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -327,7 +327,7 @@ Fn<void()> AboutGigagroupCallback(not_null<ChannelData*> channel) { channel->session().api().applyUpdates(result); Ui::hideSettingsAndLayer(); Ui::Toast::Show(tr::lng_gigagroup_done(tr::now)); - }).fail([=](const MTP::Error &error) { + }).fail([=] { *converting = false; }).send(); }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_requests_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_requests_box.cpp index 4f60cd638..02370e74a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_requests_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_requests_box.cpp @@ -340,7 +340,7 @@ void RequestsBoxController::loadMoreRows() { refreshDescription(); } delegate()->peerListRefreshRows(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _loadRequestId = 0; _allLoaded = true; }).send(); diff --git a/Telegram/SourceFiles/boxes/pin_messages_box.cpp b/Telegram/SourceFiles/boxes/pin_messages_box.cpp index 3b9718ceb..f5c4c69a0 100644 --- a/Telegram/SourceFiles/boxes/pin_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/pin_messages_box.cpp @@ -123,7 +123,7 @@ void PinMessageBox::pinMessage() { )).done([=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); Ui::hideLayer(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { Ui::hideLayer(); }).send(); } diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 6e66ce602..2b2852381 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -371,7 +371,7 @@ StickerSetBox::Inner::Inner( MTP_int(0) // hash )).done([=](const MTPmessages_StickerSet &result) { gotSet(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _loaded = true; _errors.fire(Error::NotFound); }).send(); @@ -934,7 +934,7 @@ void StickerSetBox::Inner::install() { MTP_bool(false) )).done([=](const MTPmessages_StickerSetInstallResult &result) { installDone(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _errors.fire(Error::NotFound); }).send(); } @@ -947,7 +947,7 @@ void StickerSetBox::Inner::archiveStickers() { if (result.type() == mtpc_messages_stickerSetInstallResultSuccess) { _setArchived.fire_copy(_setId); } - }).fail([](const MTP::Error &error) { + }).fail([] { Ui::Toast::Show(Lang::Hard::ServerError()); }).send(); } diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index f7e842087..763ef4101 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -1897,7 +1897,7 @@ void StickersBox::Inner::handleMegagroupSetAddressChange() { }, [](const MTPDmessages_stickerSetNotModified &) { LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _megagroupSetRequestId = 0; setMegagroupSelectedSet({}); }).send(); diff --git a/Telegram/SourceFiles/boxes/url_auth_box.cpp b/Telegram/SourceFiles/boxes/url_auth_box.cpp index 909d04747..685d3ca3c 100644 --- a/Telegram/SourceFiles/boxes/url_auth_box.cpp +++ b/Telegram/SourceFiles/boxes/url_auth_box.cpp @@ -67,7 +67,7 @@ void UrlAuthBox::Activate( Request(data, item, row, column); } }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { const auto button = HistoryMessageMarkupButton::Get( &session->data(), itemId, @@ -105,7 +105,7 @@ void UrlAuthBox::Activate( }, [&](const MTPDurlAuthResultRequest &data) { Request(data, session, url, context); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { HiddenUrlClickHandler::Open(url, context); }).send(); } @@ -165,7 +165,7 @@ void UrlAuthBox::Request( return url; }); finishWithUrl(to); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finishWithUrl(url); }).send(); } @@ -216,7 +216,7 @@ void UrlAuthBox::Request( return url; }); finishWithUrl(to); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finishWithUrl(url); }).send(); } diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 8a5455e91..85bc8ab86 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -344,7 +344,7 @@ void BoxController::loadMoreRows() { } break; default: Unexpected("Type of messages.Messages (Calls::BoxController::preloadRows)"); } - }).fail([this](const MTP::Error &error) { + }).fail([this] { _loadRequestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index a38203ff6..a592d2e64 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -1209,7 +1209,7 @@ void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) { // updates being handled, but in a guarded way. crl::on_main(weak, [=] { setState(finalState); }); session->api().applyUpdates(result); - }).fail(crl::guard(weak, [this, finalState](const MTP::Error &error) { + }).fail(crl::guard(weak, [this, finalState] { setState(finalState); })).send(); } diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 3d1750a12..d5ed48eb9 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -332,7 +332,7 @@ void Instance::refreshDhConfig() { } else { _delegate->callFailed(call); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { const auto call = weak.get(); if (!call) { return; @@ -391,7 +391,7 @@ void Instance::refreshServerConfig(not_null<Main::Session*> session) { const auto &json = result.c_dataJSON().vdata().v; UpdateConfig(std::string(json.data(), json.size())); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _serverConfigRequestSession = nullptr; }).send(); } diff --git a/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp b/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp index 655f1165e..4293feeea 100644 --- a/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp +++ b/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp @@ -467,7 +467,7 @@ void ChooseJoinAsProcess::start( _request->box = box.data(); _request->showBox(std::move(box)); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finish({ .peer = _request->peer, .joinAs = _request->peer->session().user(), diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index bcee0a6cc..b63d57804 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -1604,7 +1604,7 @@ void GroupCall::discard() { // updates being handled, but in a guarded way. crl::on_main(this, [=] { hangup(); }); _peer->session().api().applyUpdates(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { hangup(); }).send(); } @@ -1672,7 +1672,7 @@ void GroupCall::leave() { // updates being handled, but in a guarded way. crl::on_main(weak, [=] { setState(finalState); }); session->api().applyUpdates(result); - }).fail(crl::guard(weak, [=](const MTP::Error &error) { + }).fail(crl::guard(weak, [=] { setState(finalState); })).send(); } @@ -2225,7 +2225,6 @@ void GroupCall::changeTitle(const QString &title) { )).done([=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); _titleChanged.fire({}); - }).fail([=](const MTP::Error &error) { }).send(); } @@ -2258,7 +2257,7 @@ void GroupCall::toggleRecording( )).done([=](const MTPUpdates &result) { _peer->session().api().applyUpdates(result); _recordingStoppedByMe = false; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _recordingStoppedByMe = false; }).send(); } diff --git a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp index 72eb9b1d9..a532de970 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_keywords.cpp @@ -400,7 +400,7 @@ void EmojiKeywords::LangPack::refresh() { _requestId = 0; _lastRefreshTime = crl::now(); applyDifference(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; _lastRefreshTime = crl::now(); }).send(); @@ -670,7 +670,7 @@ void EmojiKeywords::refreshRemoteList() { }); }) | ranges::to_vector); _langsRequestId = 0; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _langsRequestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index dfe99608f..213fcdac1 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -854,7 +854,7 @@ void GifsListWidget::sendInlineRequest() { MTP_string(nextOffset) )).done([this](const MTPmessages_BotResults &result) { inlineResultsDone(result); - }).fail([this](const MTP::Error &error) { + }).fail([this] { // show error? _footer->setLoading(false); _inlineRequestId = 0; diff --git a/Telegram/SourceFiles/chat_helpers/send_context_menu.cpp b/Telegram/SourceFiles/chat_helpers/send_context_menu.cpp index dea870e14..9c3ac27ff 100644 --- a/Telegram/SourceFiles/chat_helpers/send_context_menu.cpp +++ b/Telegram/SourceFiles/chat_helpers/send_context_menu.cpp @@ -160,7 +160,7 @@ void SetupUnreadMentionsMenu( )).done([=](const MTPmessages_AffectedHistory &result) { state->sentForPeers.remove(peer); peer->session().api().applyAffectedHistory(peer, result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { state->sentForPeers.remove(peer); }).send(); }); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp index b1fb1a35f..d197246e8 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_dice_pack.cpp @@ -55,7 +55,7 @@ void DicePack::load() { }, [](const MTPDmessages_stickerSetNotModified &) { LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index b4adb759b..d663f4290 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -237,7 +237,7 @@ void EmojiPack::refreshAnimations() { }, [](const MTPDmessages_stickerSetNotModified &) { LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _animationsRequestId = 0; refreshDelayed(); }).send(); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index c907c5f5e..eeccc6f36 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -1106,7 +1106,6 @@ void StickersListWidget::preloadMoreOfficial() { }); resizeToWidth(width()); update(); - }).fail([=](const MTP::Error &error) { }).send(); } @@ -1276,7 +1275,7 @@ void StickersListWidget::sendSearchRequest() { MTP_long(hash) )).done([=](const MTPmessages_FoundStickerSets &result) { searchResultsDone(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { // show error? _footer->setLoading(false); _searchRequestId = 0; @@ -3149,7 +3148,7 @@ void StickersListWidget::sendInstallRequest( session().data().stickers().applyArchivedResult( result.c_messages_stickerSetInstallResultArchive()); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { notInstalledLocally(setId); session().data().stickers().undoInstallLocally(setId); }).send(); diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp index d54da1ce9..93628a42c 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.cpp +++ b/Telegram/SourceFiles/data/data_chat_filters.cpp @@ -256,7 +256,7 @@ void ChatFilters::load(bool force) { )).done([=](const MTPVector<MTPDialogFilter> &result) { received(result.v); _loadRequestId = 0; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _loadRequestId = 0; }).send(); } @@ -555,7 +555,7 @@ bool ChatFilters::loadNextExceptions(bool chatsListLoaded) { _exceptionsLoadRequestId = 0; _owner->session().data().histories().applyPeerDialogs(result); _owner->session().api().requestMoreDialogsIfNeeded(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _exceptionsLoadRequestId = 0; _owner->session().api().requestMoreDialogsIfNeeded(); }).send(); @@ -594,7 +594,7 @@ void ChatFilters::requestSuggested() { }) | ranges::to_vector; _suggestedUpdated.fire({}); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _suggestedRequestId = 0; _suggestedLastReceived = crl::now() + kRefreshSuggestedTimeout / 2; diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp index 315e30e10..15e07a476 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.cpp +++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp @@ -192,7 +192,7 @@ void CloudThemes::reloadCurrent() { MTP_long(fields.documentId) )).done([=](const MTPTheme &result) { applyUpdate(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _reloadCurrentTimer.callOnce(kReloadTimeout); }).send(); } @@ -345,7 +345,7 @@ void CloudThemes::refresh() { _updates.fire({}); }, [](const MTPDaccount_themesNotModified &) { }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _refreshRequestId = 0; }).send(); } @@ -373,7 +373,7 @@ void CloudThemes::refreshChatThemes() { _chatThemesUpdates.fire({}); }, [](const MTPDaccount_themesNotModified &) { }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _chatThemesRequestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index f3ef08f80..3139e752d 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -135,7 +135,7 @@ void GroupCall::requestParticipants() { _participantsReloaded.fire({}); } }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _participantsRequestId = 0; const auto reloaded = processSavedFullCall(); setServerParticipantsCount(_participants.size()); @@ -511,7 +511,7 @@ void GroupCall::reload() { } _reloadRequestId = 0; processFullCall(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _reloadRequestId = 0; }).send(); } @@ -898,7 +898,7 @@ void GroupCall::requestUnknownParticipants() { _participantsResolved.fire(&ssrcs); } requestUnknownParticipants(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _unknownParticipantPeersRequestId = 0; for (const auto &[ssrc, when] : ssrcs) { _unknownSpokenSsrcs.remove(ssrc); diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 21836dc17..cafcd577a 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -268,7 +268,7 @@ void Histories::requestDialogEntry(not_null<Data::Folder*> folder) { )).done([=](const MTPmessages_PeerDialogs &result) { applyPeerDialogs(result); _dialogFolderRequests.remove(folder); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _dialogFolderRequests.remove(folder); }).send(); } @@ -348,7 +348,7 @@ void Histories::sendDialogRequests() { )).done([=](const MTPmessages_PeerDialogs &result) { applyPeerDialogs(result); finalize(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finalize(); }).send(); } @@ -428,7 +428,7 @@ void Histories::requestFakeChatListMessage( _fakeChatListRequests.erase(history); history->setFakeChatListMessageFrom(result); finish(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _fakeChatListRequests.erase(history); history->setFakeChatListMessageFrom(MTP_messages_messages( MTP_vector<MTPMessage>(0), @@ -472,7 +472,7 @@ void Histories::requestGroupAround(not_null<HistoryItem*> item) { history->channelId(), result); finish(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _chatListGroupRequests.remove(history); finish(); }).send(); @@ -555,7 +555,7 @@ void Histories::sendReadRequest(not_null<History*> history, State &state) { MTP_int(tillId) )).done([=](const MTPBool &result) { finished(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finished(); }).send(); } else { @@ -565,7 +565,7 @@ void Histories::sendReadRequest(not_null<History*> history, State &state) { )).done([=](const MTPmessages_AffectedMessages &result) { session().api().applyAffectedMessages(history->peer, result); finished(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finished(); }).send(); } @@ -610,20 +610,17 @@ void Histories::deleteMessages( finish(); history->requestChatListMessage(); }; - const auto fail = [=](const MTP::Error &error) { - finish(); - }; if (const auto channel = history->peer->asChannel()) { return session().api().request(MTPchannels_DeleteMessages( channel->inputChannel, MTP_vector<MTPint>(ids) - )).done(done).fail(fail).send(); + )).done(done).fail(finish).send(); } else { using Flag = MTPmessages_DeleteMessages::Flag; return session().api().request(MTPmessages_DeleteMessages( MTP_flags(revoke ? Flag::f_revoke : Flag(0)), MTP_vector<MTPint>(ids) - )).done(done).fail(fail).send(); + )).done(done).fail(finish).send(); } }); } @@ -635,9 +632,6 @@ void Histories::deleteAllMessages( bool revoke) { sendRequest(history, RequestType::Delete, [=](Fn<void()> finish) { const auto peer = history->peer; - const auto fail = [=](const MTP::Error &error) { - finish(); - }; const auto chat = peer->asChat(); const auto channel = peer->asChannel(); if (!justClear && revoke && channel && channel->canDelete()) { @@ -656,7 +650,7 @@ void Histories::deleteAllMessages( MTP_int(deleteTillId) )).done([=](const MTPBool &result) { finish(); - }).fail(fail).send(); + }).fail(finish).send(); } else if (revoke && chat && chat->amCreator()) { return session().api().request(MTPmessages_DeleteChat( chat->inputChat @@ -704,7 +698,7 @@ void Histories::deleteAllMessages( revoke); } finish(); - }).fail(fail).send(); + }).fail(finish).send(); } }); } @@ -734,9 +728,6 @@ void Histories::deleteMessagesByDates( bool revoke) { sendRequest(history, RequestType::Delete, [=](Fn<void()> finish) { const auto peer = history->peer; - const auto fail = [=](const MTP::Error &error) { - finish(); - }; using Flag = MTPmessages_DeleteHistory::Flag; const auto flags = Flag::f_just_clear | Flag::f_min_date @@ -756,7 +747,7 @@ void Histories::deleteMessagesByDates( deleteMessagesByDates(history, minDate, maxDate, revoke); } finish(); - }).fail(fail).send(); + }).fail(finish).send(); }); history->destroyMessagesByDates(minDate, maxDate); } diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp index 348374654..f02ad8f29 100644 --- a/Telegram/SourceFiles/data/data_replies_list.cpp +++ b/Telegram/SourceFiles/data/data_replies_list.cpp @@ -461,7 +461,7 @@ void RepliesList::loadAround(MsgId id) { _skippedBefore = 0; } } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _beforeId = 0; _loadingAround = std::nullopt; finish(); @@ -509,7 +509,7 @@ void RepliesList::loadBefore() { _fullCount = _list.size(); } } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _beforeId = 0; finish(); }).send(); @@ -553,7 +553,7 @@ void RepliesList::loadAfter() { _fullCount = _list.size(); } } - }).fail([=](const MTP::Error &error) { + }).fail([=] { _afterId = 0; finish(); }).send(); diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index f90a0da49..292a332d6 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -389,7 +389,7 @@ void ScheduledMessages::request(not_null<History*> history) { MTP_long(hash)) ).done([=](const MTPmessages_Messages &result) { parse(history, result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requests.remove(history); }).send(); } diff --git a/Telegram/SourceFiles/data/data_search_controller.cpp b/Telegram/SourceFiles/data/data_search_controller.cpp index 1bd42b218..cbaad409f 100644 --- a/Telegram/SourceFiles/data/data_search_controller.cpp +++ b/Telegram/SourceFiles/data/data_search_controller.cpp @@ -391,7 +391,7 @@ void SearchController::requestMore( parsed.noSkipRange, parsed.fullCount); finish(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finish(); }).send(); }); diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp index 1dc5b49d1..155dc5f90 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp +++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp @@ -124,7 +124,7 @@ void SponsoredMessages::request(not_null<History*> history) { channel->inputChannel) ).done([=](const MTPmessages_sponsoredMessages &result) { parse(history, result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requests.remove(history); }).send(); } @@ -228,7 +228,7 @@ void SponsoredMessages::view(const FullMsgId &fullId) { auto &request = _viewRequests[randomId]; request.lastReceived = crl::now(); request.requestId = 0; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _viewRequests.remove(randomId); }).send(); } diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index 86ed5b3a4..6a8988855 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -539,7 +539,7 @@ void Stickers::requestSetToPushFaved(not_null<DocumentData*> document) { }, [](const MTPDmessages_stickerSetNotModified &) { LOG(("API Error: Unexpected messages.stickerSetNotModified.")); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { // Perhaps this is a deleted sticker pack. Add anyway. addAnyway({}); }).send(); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 6041feaa9..f226f70f3 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -767,7 +767,7 @@ void InnerWidget::preloadMore(Direction direction) { if (!loadedFlag) { addEvents(direction, results.vevents().v); } - }).fail([this, &requestId, &loadedFlag](const MTP::Error &error) { + }).fail([this, &requestId, &loadedFlag] { requestId = 0; loadedFlag = true; update(); @@ -1431,7 +1431,7 @@ void InnerWidget::suggestRestrictParticipant( || (type == mtpc_channelParticipantCreator); editRestrictions(hasAdminRights, ChatRestrictionsInfo()); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { editRestrictions(false, ChatRestrictionsInfo()); }).send(); } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 601133d0b..ab8c7f616 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -321,7 +321,7 @@ void FastShareMessage(not_null<HistoryItem*> item) { Ui::hideLayer(); } finish(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { finish(); }).afterRequest(history->sendRequestId).send(); return history->sendRequestId; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp index 3e432e5e6..61ec114ed 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_ttl_button.cpp @@ -78,7 +78,7 @@ void AutoDeleteSettingsBox( if (const auto strong = state->weak.data()) { strong->closeBox(); } - }).fail([=](const MTP::Error &error) { + }).fail([=] { state->savingRequestId = 0; }).send(); }; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index a4767536d..cb918c0ee 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -1124,7 +1124,6 @@ void SendReport( MTP_string(comment) )).done([=](const MTPBool &result) { Ui::Toast::Show(tr::lng_report_thanks(tr::now)); - }).fail([=](const MTP::Error &error) { }).send(); } else { auto apiIds = QVector<MTPint>(); @@ -1139,7 +1138,6 @@ void SendReport( MTP_string(comment) )).done([=](const MTPBool &result) { Ui::Toast::Show(tr::lng_report_thanks(tr::now)); - }).fail([=](const MTP::Error &error) { }).send(); } } diff --git a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp index 920f34ef2..a5a55e504 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp +++ b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp @@ -298,7 +298,7 @@ void ListController::loadMoreRows() { delegate()->peerListRefreshRows(); } _loadRequestId = 0; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _loadRequestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index d9d4123ca..0f317844a 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -447,7 +447,7 @@ void Widget::onInlineRequest() { MTP_string(nextOffset) )).done([=](const MTPmessages_BotResults &result) { inlineResultsDone(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { // show error? _requesting.fire(false); _inlineRequestId = 0; diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp index 6634ec32f..c626ea867 100644 --- a/Telegram/SourceFiles/intro/intro_step.cpp +++ b/Telegram/SourceFiles/intro/intro_step.cpp @@ -166,7 +166,7 @@ void Step::finish(const MTPUser &user, QImage &&photo) { api().request(MTPmessages_GetDialogFilters( )).done([=](const MTPVector<MTPDialogFilter> &result) { createSession(user, photo, result.v); - }).fail([=](const MTP::Error &error) { + }).fail([=] { createSession(user, photo, QVector<MTPDialogFilter>()); }).send(); } diff --git a/Telegram/SourceFiles/lang/lang_cloud_manager.cpp b/Telegram/SourceFiles/lang/lang_cloud_manager.cpp index af1654626..3719af18c 100644 --- a/Telegram/SourceFiles/lang/lang_cloud_manager.cpp +++ b/Telegram/SourceFiles/lang/lang_cloud_manager.cpp @@ -234,7 +234,7 @@ void CloudManager::requestLangPackDifference(Pack pack) { )).done([=](const MTPLangPackDifference &result) { packRequestId(pack) = 0; applyLangPackDifference(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { packRequestId(pack) = 0; }).send(); } else { @@ -244,7 +244,7 @@ void CloudManager::requestLangPackDifference(Pack pack) { )).done([=](const MTPLangPackDifference &result) { packRequestId(pack) = 0; applyLangPackDifference(result); - }).fail([=](const MTP::Error &error) { + }).fail([=] { packRequestId(pack) = 0; }).send(); } @@ -322,7 +322,7 @@ void CloudManager::requestLanguageList() { _languageListChanged.fire({}); } _languagesRequestId = 0; - }).fail([=](const MTP::Error &error) { + }).fail([=] { _languagesRequestId = 0; }).send(); } @@ -509,7 +509,7 @@ void CloudManager::switchToLanguage(const Language &data) { tr::lng_cancel(tr::now), [=] { performSwitchAndRestart(data); }), Ui::LayerOption::KeepOther); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _getKeysForSwitchRequestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/main/main_app_config.cpp b/Telegram/SourceFiles/main/main_app_config.cpp index 5c43c0bb9..fef8d9702 100644 --- a/Telegram/SourceFiles/main/main_app_config.cpp +++ b/Telegram/SourceFiles/main/main_app_config.cpp @@ -51,7 +51,7 @@ void AppConfig::refresh() { DEBUG_LOG(("getAppConfig result handled.")); } _refreshed.fire({}); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _requestId = 0; refreshDelayed(); }).send(); diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index f2bc757f5..887927553 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -1038,7 +1038,7 @@ void FormController::cancelPassword() { )).done([=](const MTPBool &result) { _passwordRequestId = 0; reloadPassword(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { _passwordRequestId = 0; reloadPassword(); }).send(); @@ -1259,7 +1259,7 @@ rpl::producer<EditDocumentCountry> FormController::preferredLanguage( }); consumer.put_next({ countryCode, findLang() }); consumer.put_done(); - }).fail([=](const MTP::Error &error) { + }).fail([=] { consumer.put_next({ countryCode, QString() }); consumer.put_done(); }).send(); diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 2339e7b02..43bf1e9ec 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -329,7 +329,7 @@ void SetupHelp( Ui::showPeerHistory(user, ShowAtUnreadMsgId); } }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { *requestId = 0; }).send(); }); diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp index 21b9455ab..ca112783e 100644 --- a/Telegram/SourceFiles/support/support_helper.cpp +++ b/Telegram/SourceFiles/support/support_helper.cpp @@ -263,7 +263,7 @@ Helper::Helper(not_null<Main::Session*> session) result.match([&](const MTPDhelp_supportName &data) { setSupportName(qs(data.vname())); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { setSupportName( qsl("[rand^") + QString::number(Core::Sandbox::Instance().installationTag()) @@ -529,7 +529,7 @@ void Helper::saveInfo( )).done([=](const MTPhelp_UserInfo &result) { applyInfo(user, result); done(true); - }).fail([=](const MTP::Error &error) { + }).fail([=] { done(false); }).send(); } diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 3f736ba09..c6a7b88bf 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -743,7 +743,7 @@ void SaveTheme( result.match([&](const MTPDtheme &data) { save(CloudTheme::Parse(&window->account().session(), data)); }); - }).fail([=](const MTP::Error &error) { + }).fail([=] { save(CloudTheme()); }).send(); } else { diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 7c0c591ba..cf84f7124 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -240,9 +240,7 @@ void SessionNavigation::resolveChannelById( fail(); } }); - }).fail([=](const MTP::Error &error) { - fail(); - }).send(); + }).fail(fail).send(); } void SessionNavigation::showPeerByLinkResolved( @@ -301,9 +299,7 @@ void SessionNavigation::showPeerByLinkResolved( } else { bad(); } - }).fail([=](const MTP::Error &error) { - bad(); - }).send(); + }).fail(bad).send(); }).send(); return; } From 69dc6c98cefafa0741de059570aab4e726d0b891 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 26 Nov 2021 23:54:46 +0300 Subject: [PATCH 143/180] Removed unused MTPBool from callbacks on done. --- .../SourceFiles/api/api_authorizations.cpp | 4 ++-- Telegram/SourceFiles/api/api_blocked_peers.cpp | 4 ++-- Telegram/SourceFiles/api/api_confirm_phone.cpp | 2 +- Telegram/SourceFiles/api/api_invite_links.cpp | 4 ++-- Telegram/SourceFiles/api/api_self_destruct.cpp | 2 +- .../SourceFiles/api/api_sensitive_content.cpp | 2 +- Telegram/SourceFiles/api/api_updates.cpp | 2 +- Telegram/SourceFiles/apiwrap.cpp | 14 +++++--------- Telegram/SourceFiles/boxes/add_contact_box.cpp | 4 ++-- Telegram/SourceFiles/boxes/passcode_box.cpp | 18 +++++++++--------- .../boxes/peers/edit_participants_box.cpp | 2 +- .../boxes/peers/edit_peer_info_box.cpp | 6 +++--- Telegram/SourceFiles/calls/calls_call.cpp | 2 +- Telegram/SourceFiles/data/data_histories.cpp | 14 +++----------- .../history/view/history_view_context_menu.cpp | 4 ++-- .../view/history_view_replies_section.cpp | 2 +- Telegram/SourceFiles/intro/intro_widget.cpp | 2 +- .../passport/passport_form_controller.cpp | 10 +++++----- .../SourceFiles/window/window_peer_menu.cpp | 2 +- 19 files changed, 44 insertions(+), 56 deletions(-) diff --git a/Telegram/SourceFiles/api/api_authorizations.cpp b/Telegram/SourceFiles/api/api_authorizations.cpp index a360d8e78..143166b78 100644 --- a/Telegram/SourceFiles/api/api_authorizations.cpp +++ b/Telegram/SourceFiles/api/api_authorizations.cpp @@ -194,7 +194,7 @@ void Authorizations::updateTTL(int days) { _api.request(_ttlRequestId).cancel(); _ttlRequestId = _api.request(MTPaccount_SetAuthorizationTTL( MTP_int(days) - )).done([=](const MTPBool &result) { + )).done([=] { _ttlRequestId = 0; }).fail([=] { _ttlRequestId = 0; @@ -216,7 +216,7 @@ void Authorizations::toggleCallsDisabled(uint64 hash, bool disabled) { MTP_long(hash), MTPBool(), // encrypted_requests_disabled MTP_bool(disabled) - )).done([=](const MTPBool &) { + )).done([=] { _toggleCallsDisabledRequests.remove(hash); }).fail([=] { _toggleCallsDisabledRequests.remove(hash); diff --git a/Telegram/SourceFiles/api/api_blocked_peers.cpp b/Telegram/SourceFiles/api/api_blocked_peers.cpp index 56ec63062..c28b9c37f 100644 --- a/Telegram/SourceFiles/api/api_blocked_peers.cpp +++ b/Telegram/SourceFiles/api/api_blocked_peers.cpp @@ -80,7 +80,7 @@ void BlockedPeers::block(not_null<PeerData*> peer) { } else if (_blockRequests.find(peer) == end(_blockRequests)) { const auto requestId = _api.request(MTPcontacts_Block( peer->input - )).done([=](const MTPBool &result) { + )).done([=] { _blockRequests.erase(peer); peer->setIsBlocked(true); if (_slice) { @@ -109,7 +109,7 @@ void BlockedPeers::unblock(not_null<PeerData*> peer, Fn<void()> onDone) { } const auto requestId = _api.request(MTPcontacts_Unblock( peer->input - )).done([=](const MTPBool &result) { + )).done([=] { _blockRequests.erase(peer); peer->setIsBlocked(false); if (_slice) { diff --git a/Telegram/SourceFiles/api/api_confirm_phone.cpp b/Telegram/SourceFiles/api/api_confirm_phone.cpp index 559f16136..0db192021 100644 --- a/Telegram/SourceFiles/api/api_confirm_phone.cpp +++ b/Telegram/SourceFiles/api/api_confirm_phone.cpp @@ -83,7 +83,7 @@ void ConfirmPhone::resolve( _checkRequestId = _api.request(MTPaccount_ConfirmPhone( MTP_string(phoneHash), MTP_string(code) - )).done([=](const MTPBool &result) { + )).done([=] { _checkRequestId = 0; controller->show( Box<Ui::InformBox>( diff --git a/Telegram/SourceFiles/api/api_invite_links.cpp b/Telegram/SourceFiles/api/api_invite_links.cpp index 8206c6bb4..3aad8c14e 100644 --- a/Telegram/SourceFiles/api/api_invite_links.cpp +++ b/Telegram/SourceFiles/api/api_invite_links.cpp @@ -363,7 +363,7 @@ void InviteLinks::destroy( _api->request(MTPmessages_DeleteExportedChatInvite( peer->input, MTP_string(link) - )).done([=](const MTPBool &result) { + )).done([=] { const auto callbacks = _deleteCallbacks.take(key); if (callbacks) { for (const auto &callback : *callbacks) { @@ -398,7 +398,7 @@ void InviteLinks::destroyAllRevoked( _api->request(MTPmessages_DeleteRevokedExportedChatInvites( peer->input, admin->inputUser - )).done([=](const MTPBool &result) { + )).done([=] { if (const auto callbacks = _deleteRevokedCallbacks.take(peer)) { for (const auto &callback : *callbacks) { callback(); diff --git a/Telegram/SourceFiles/api/api_self_destruct.cpp b/Telegram/SourceFiles/api/api_self_destruct.cpp index 6247f3037..fb8497637 100644 --- a/Telegram/SourceFiles/api/api_self_destruct.cpp +++ b/Telegram/SourceFiles/api/api_self_destruct.cpp @@ -40,7 +40,7 @@ void SelfDestruct::update(int days) { _api.request(_requestId).cancel(); _requestId = _api.request(MTPaccount_SetAccountTTL( MTP_accountDaysTTL(MTP_int(days)) - )).done([=](const MTPBool &result) { + )).done([=] { _requestId = 0; }).fail([=] { _requestId = 0; diff --git a/Telegram/SourceFiles/api/api_sensitive_content.cpp b/Telegram/SourceFiles/api/api_sensitive_content.cpp index d12084dbe..e2d3e49ed 100644 --- a/Telegram/SourceFiles/api/api_sensitive_content.cpp +++ b/Telegram/SourceFiles/api/api_sensitive_content.cpp @@ -61,7 +61,7 @@ void SensitiveContent::update(bool enabled) { _api.request(_requestId).cancel(); _requestId = _api.request(MTPaccount_SetContentSettings( MTP_flags(enabled ? Flag::f_sensitive_enabled : Flag(0)) - )).done([=](const MTPBool &result) { + )).done([=] { _requestId = 0; }).fail([=] { _requestId = 0; diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index aa764490b..fb97541c2 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -919,7 +919,7 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) { } else { _onlineRequest = api().request(MTPaccount_UpdateStatus( MTP_bool(!isOnline) - )).done([=](const MTPBool &result) { + )).done([=] { Core::App().quitPreventFinished(); }).fail([=] { Core::App().quitPreventFinished(); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index b7567a5a4..cf4c75129 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -333,7 +333,7 @@ void ApiWrap::requestTermsUpdate() { void ApiWrap::acceptTerms(bytes::const_span id) { request(MTPhelp_AcceptTermsOfService( MTP_dataJSON(MTP_bytes(id)) - )).done([=](const MTPBool &result) { + )).done([=] { requestTermsUpdate(); }).send(); } @@ -1478,7 +1478,7 @@ void ApiWrap::saveStickerSets( reorderRequestId() = request(MTPmessages_ReorderStickerSets( MTP_flags(flags), MTP_vector<MTPlong>(mtpOrder) - )).done([=](const MTPBool &result) { + )).done([=] { reorderRequestId() = 0; }).fail([=] { reorderRequestId() = 0; @@ -1551,11 +1551,7 @@ void ApiWrap::saveStickerSets( }; requestId = request(MTPmessages_ClearRecentStickers( MTP_flags(flags) - )).done([=](const MTPBool &result) { - finish(); - }).fail([=] { - finish(); - }).send(); + )).done(finish).fail(finish).send(); continue; } @@ -2799,7 +2795,7 @@ void ApiWrap::readFeaturedSets() { if (!wrappedIds.empty()) { auto requestData = MTPmessages_ReadFeaturedStickers( MTP_vector<MTPlong>(wrappedIds)); - request(std::move(requestData)).done([=](const MTPBool &result) { + request(std::move(requestData)).done([=] { local().writeFeaturedStickers(); _session->data().stickers().notifyUpdated(); }).send(); @@ -4056,7 +4052,7 @@ void ApiWrap::saveContactSignupSilent(bool silent) { const auto requestId = request(MTPaccount_SetContactSignUpNotification( MTP_bool(silent) - )).done([=](const MTPBool &) { + )).done([=] { _contactSignupSilentRequestId = 0; _contactSignupSilent = silent; _contactSignupSilentChanges.fire_copy(silent); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 09d4df8ab..611df771c 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -1112,7 +1112,7 @@ void SetupChannelBox::save() { _saveRequestId = _api.request(MTPchannels_UpdateUsername( _channel->inputChannel, MTP_string(_sentUsername) - )).done([=](const MTPBool &result) { + )).done([=] { _channel->setName( TextUtilities::SingleLine(_channel->name), _sentUsername); @@ -1604,7 +1604,7 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) { _revokeRequestId = _api.request(MTPchannels_UpdateUsername( pressed->asChannel()->inputChannel, MTP_string() - )).done([=, close = std::move(close)](const MTPBool &result) { + )).done([=, close = std::move(close)] { close(); if (const auto callback = _revokeCallback) { callback(); diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index 5d8ab1f28..46a9385fe 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -546,7 +546,7 @@ void PasscodeBox::validateEmail( } _setRequest = _api.request(MTPaccount_ConfirmPasswordEmail( MTP_string(code) - )).done([=](const MTPBool &result) { + )).done([=] { *set = true; setPasswordDone(newPasswordBytes); }).fail([=](const MTP::Error &error) { @@ -575,7 +575,7 @@ void PasscodeBox::validateEmail( return; } _setRequest = _api.request(MTPaccount_ResendPasswordEmail( - )).done([=](const MTPBool &result) { + )).done([=] { _setRequest = 0; resent->fire(tr::lng_cloud_password_resent(tr::now)); }).fail([=] { @@ -830,7 +830,7 @@ void PasscodeBox::sendClearCloudPassword( MTP_string(hint), MTP_string(email), MTPSecureSecretSettings()) - )).done([=](const MTPBool &result) { + )).done([=] { setPasswordDone({}); }).fail([=](const MTP::Error &error) mutable { setPasswordFail({}, QString(), error); @@ -865,7 +865,7 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) { _setRequest = _api.request(MTPaccount_UpdatePasswordSettings( MTP_inputCheckPasswordEmpty(), settings - )).done([=](const MTPBool &result) { + )).done([=] { setPasswordDone(newPasswordBytes); }).fail([=](const MTP::Error &error) { setPasswordFail(newPasswordBytes, email, error); @@ -972,7 +972,7 @@ void PasscodeBox::resetSecret( MTP_securePasswordKdfAlgoUnknown(), // secure_algo MTP_bytes(), // secure_secret MTP_long(0))) // secure_secret_id - )).done([=](const MTPBool &result) { + )).done([=] { _setRequest = 0; callback(); checkPasswordHash([=](const Core::CloudPasswordResult &check) { @@ -1026,7 +1026,7 @@ void PasscodeBox::sendChangeCloudPassword( Core::PrepareSecureSecretAlgo(_cloudFields.newSecureSecretAlgo), MTP_bytes(newSecureSecret), MTP_long(newSecureSecretId))) - )).done([=](const MTPBool &result) { + )).done([=] { setPasswordDone(newPasswordBytes); }).fail([=](const MTP::Error &error) { setPasswordFail(newPasswordBytes, QString(), error); @@ -1257,7 +1257,7 @@ void RecoverBox::submit() { // From "Change cloud password". _submitRequest = _api.request(MTPauth_CheckRecoveryPassword( MTP_string(code) - )).done([=](const MTPBool &result) { + )).done([=] { proceedToChange(code); }).fail([=](const MTP::Error &error) { checkSubmitFail(error); @@ -1378,7 +1378,7 @@ RecoveryEmailValidation ConfirmRecoveryEmail( } *requestId = session->api().request(MTPaccount_ConfirmPasswordEmail( MTP_string(code) - )).done([=](const MTPBool &result) { + )).done([=] { *requestId = 0; reloads->fire({}); if (*weak) { @@ -1412,7 +1412,7 @@ RecoveryEmailValidation ConfirmRecoveryEmail( return; } *requestId = session->api().request(MTPaccount_ResendPasswordEmail( - )).done([=](const MTPBool &result) { + )).done([=] { *requestId = 0; resent->fire(tr::lng_cloud_password_resent(tr::now)); }).fail([=] { diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index ad09dde7d..cd0c9df29 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -101,7 +101,7 @@ void SaveChatAdmin( chat->inputChat, user->inputUser, MTP_bool(isAdmin) - )).done([=](const MTPBool &result) { + )).done([=] { chat->applyEditAdmin(user, isAdmin); if (onDone) { onDone(); diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index 84fae3ba8..c9fca8adf 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -1303,7 +1303,7 @@ void Controller::saveUsername() { _api.request(MTPchannels_UpdateUsername( channel->inputChannel, MTP_string(*_savingData.username) - )).done([=](const MTPBool &result) { + )).done([=] { channel->setName( TextUtilities::SingleLine(channel->name), *_savingData.username); @@ -1358,7 +1358,7 @@ void Controller::saveLinkedChat() { _api.request(MTPchannels_SetDiscussionGroup( (channel->isBroadcast() ? channel->inputChannel : input), (channel->isBroadcast() ? input : channel->inputChannel) - )).done([=](const MTPBool &result) { + )).done([=] { channel->setLinkedChat(*_savingData.linkedChat); continueSave(); }).fail([=] { @@ -1425,7 +1425,7 @@ void Controller::saveDescription() { _api.request(MTPmessages_EditChatAbout( _peer->input, MTP_string(*_savingData.description) - )).done([=](const MTPBool &result) { + )).done([=] { successCallback(); }).fail([=](const MTP::Error &error) { const auto &type = error.type(); diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index a592d2e64..fde79072e 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -286,7 +286,7 @@ void Call::startIncoming() { _api.request(MTPphone_ReceivedCall( MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)) - )).done([=](const MTPBool &result) { + )).done([=] { if (_state.current() == State::Starting) { setState(State::WaitingIncoming); } diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index cafcd577a..b6c1f2a26 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -553,11 +553,7 @@ void Histories::sendReadRequest(not_null<History*> history, State &state) { return session().api().request(MTPchannels_ReadHistory( channel->inputChannel, MTP_int(tillId) - )).done([=](const MTPBool &result) { - finished(); - }).fail([=] { - finished(); - }).send(); + )).done(finished).fail(finished).send(); } else { return session().api().request(MTPmessages_ReadHistory( history->peer->input, @@ -648,15 +644,11 @@ void Histories::deleteAllMessages( return session().api().request(MTPchannels_DeleteHistory( channel->inputChannel, MTP_int(deleteTillId) - )).done([=](const MTPBool &result) { - finish(); - }).fail(finish).send(); + )).done(finish).fail(finish).send(); } else if (revoke && chat && chat->amCreator()) { return session().api().request(MTPmessages_DeleteChat( chat->inputChat - )).done([=](const MTPBool &result) { - finish(); - }).fail([=](const MTP::Error &error) { + )).done(finish).fail([=](const MTP::Error &error) { if (error.type() == "PEER_ID_INVALID") { // Try to join and delete, // while delete fails for non-joined. diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index cb918c0ee..94656658d 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -1122,7 +1122,7 @@ void SendReport( peer->input, apiReason, MTP_string(comment) - )).done([=](const MTPBool &result) { + )).done([=] { Ui::Toast::Show(tr::lng_report_thanks(tr::now)); }).send(); } else { @@ -1136,7 +1136,7 @@ void SendReport( MTP_vector<MTPint>(apiIds), apiReason, MTP_string(comment) - )).done([=](const MTPBool &result) { + )).done([=] { Ui::Toast::Show(tr::lng_report_thanks(tr::now)); }).send(); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index bee37c0e4..454403c41 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -353,7 +353,7 @@ void RepliesWidget::sendReadTillRequest() { _root->history()->peer->input, MTP_int(_root->id), MTP_int(_root->computeRepliesInboxReadTillFull()) - )).done(crl::guard(this, [=](const MTPBool &) { + )).done(crl::guard(this, [=] { _readRequestId = 0; reloadUnreadCountIfNeeded(); })).send(); diff --git a/Telegram/SourceFiles/intro/intro_widget.cpp b/Telegram/SourceFiles/intro/intro_widget.cpp index aa3ad25a9..82d33af46 100644 --- a/Telegram/SourceFiles/intro/intro_widget.cpp +++ b/Telegram/SourceFiles/intro/intro_widget.cpp @@ -493,7 +493,7 @@ void Widget::resetAccount() { } _resetRequest = _api->request(MTPaccount_DeleteAccount( MTP_string("Forgot password") - )).done([=](const MTPBool &result) { + )).done([=] { _resetRequest = 0; Ui::hideLayer(); diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 887927553..9df983fa9 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -747,7 +747,7 @@ std::vector<not_null<const Value*>> FormController::submitGetErrors() { MTP_bytes(credentialsEncryptedData.bytes), MTP_bytes(credentialsEncryptedData.hash), MTP_bytes(credentialsEncryptedSecret)) - )).done([=](const MTPBool &result) { + )).done([=] { _submitRequestId = 0; _submitSuccess = true; @@ -1035,7 +1035,7 @@ void FormController::cancelPassword() { return; } _passwordRequestId = _api.request(MTPaccount_CancelPasswordEmail( - )).done([=](const MTPBool &result) { + )).done([=] { _passwordRequestId = 0; reloadPassword(); }).fail([=] { @@ -1115,7 +1115,7 @@ void FormController::resetSecret( MTP_securePasswordKdfAlgoUnknown(), // secure_algo MTP_bytes(), // secure_secret MTP_long(0))) // secure_secret_id - )).done([=](const MTPBool &result) { + )).done([=] { _saveSecretRequestId = 0; generateSecret(password); }).fail([=](const MTP::Error &error) { @@ -1941,7 +1941,7 @@ void FormController::deleteValueEdit(not_null<const Value*> value) { const auto nonconst = findValue(value); nonconst->saveRequestId = _api.request(MTPaccount_DeleteSecureValue( MTP_vector<MTPSecureValueType>(1, ConvertType(nonconst->type)) - )).done([=](const MTPBool &result) { + )).done([=] { resetValue(*nonconst); _valueSaveFinished.fire_copy(value); }).fail([=](const MTP::Error &error) { @@ -2297,7 +2297,7 @@ void FormController::saveSecret( Core::PrepareSecureSecretAlgo(_password.newSecureAlgo), MTP_bytes(encryptedSecret), MTP_long(saved.secretId))) - )).done([=](const MTPBool &result) { + )).done([=] { session().data().rememberPassportCredentials( std::move(saved), kRememberCredentialsDelay); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index b329eab3c..13fa45686 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -215,7 +215,7 @@ void TogglePinnedDialog( history->session().api().request(MTPmessages_ToggleDialogPin( MTP_flags(flags), MTP_inputDialogPeer(key.history()->peer->input) - )).done([=](const MTPBool &result) { + )).done([=] { owner->notifyPinnedDialogsOrderUpdated(); }).send(); } else if (const auto folder = key.folder()) { From 0dfb77435d49fc1a4a665f8606c793c63e8c4364 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 28 Nov 2021 00:49:17 +0300 Subject: [PATCH 144/180] Slightly refactored add_contact_box. --- .../SourceFiles/boxes/add_contact_box.cpp | 120 +++++++++++------- Telegram/SourceFiles/boxes/add_contact_box.h | 16 ++- 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 611df771c..d0b63284f 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -128,7 +128,7 @@ void ShowAddParticipantsError( const QString &error, not_null<PeerData*> chat, const std::vector<not_null<UserData*>> &users) { - if (error == qstr("USER_BOT")) { + if (error == u"USER_BOT"_q) { const auto channel = chat->asChannel(); if ((users.size() == 1) && users.front()->isBot() @@ -168,28 +168,28 @@ void ShowAddParticipantsError( } const auto hasBot = ranges::any_of(users, &UserData::isBot); const auto text = [&] { - if (error == qstr("USER_BOT")) { + if (error == u"USER_BOT"_q) { return tr::lng_cant_invite_bot_to_channel(tr::now); - } else if (error == qstr("USER_LEFT_CHAT")) { + } else if (error == u"USER_LEFT_CHAT"_q) { // Trying to return a user who has left. - } else if (error == qstr("USER_KICKED")) { + } else if (error == u"USER_KICKED"_q) { // Trying to return a user who was kicked by admin. return tr::lng_cant_invite_banned(tr::now); - } else if (error == qstr("USER_PRIVACY_RESTRICTED")) { + } else if (error == u"USER_PRIVACY_RESTRICTED"_q) { return tr::lng_cant_invite_privacy(tr::now); - } else if (error == qstr("USER_NOT_MUTUAL_CONTACT")) { + } else if (error == u"USER_NOT_MUTUAL_CONTACT"_q) { // Trying to return user who does not have me in contacts. return tr::lng_failed_add_not_mutual(tr::now); - } else if (error == qstr("USER_ALREADY_PARTICIPANT") && hasBot) { + } else if (error == u"USER_ALREADY_PARTICIPANT"_q && hasBot) { return tr::lng_bot_already_in_group(tr::now); - } else if (error == qstr("BOT_GROUPS_BLOCKED")) { + } else if (error == u"BOT_GROUPS_BLOCKED"_q) { return tr::lng_error_cant_add_bot(tr::now); - } else if (error == qstr("PEER_FLOOD")) { + } else if (error == u"PEER_FLOOD"_q) { const auto type = (chat->isChat() || chat->isMegagroup()) ? PeerFloodType::InviteGroup : PeerFloodType::InviteChannel; return PeerFloodErrorText(&chat->session(), type); - } else if (error == qstr("ADMINS_TOO_MUCH")) { + } else if (error == u"ADMINS_TOO_MUCH"_q) { return ((chat->isChat() || chat->isMegagroup()) ? tr::lng_error_admin_limit : tr::lng_error_admin_limit_channel)(tr::now); @@ -609,9 +609,11 @@ void GroupInfoBox::createGroup( not_null<PeerListBox*> selectUsersBox, const QString &title, const std::vector<not_null<PeerData*>> &users) { - if (_creationRequestId) return; - - auto inputs = QVector<MTPInputUser>(); + if (_creationRequestId) { + return; + } + using TLUsers = MTPInputUser; + auto inputs = QVector<TLUsers>(); inputs.reserve(users.size()); for (auto peer : users) { auto user = peer->asUser(); @@ -624,7 +626,7 @@ void GroupInfoBox::createGroup( return; } _creationRequestId = _api.request(MTPmessages_CreateChat( - MTP_vector<MTPInputUser>(inputs), + MTP_vector<TLUsers>(inputs), MTP_string(title) )).done([=](const MTPUpdates &result) { auto image = _photo->takeResultImage(); @@ -633,26 +635,28 @@ void GroupInfoBox::createGroup( Ui::hideLayer(); // Destroys 'this'. ChatCreateDone(navigation, std::move(image), result); }).fail([=](const MTP::Error &error) { + const auto &type = error.type(); _creationRequestId = 0; - if (error.type() == qstr("NO_CHAT_TITLE")) { - auto weak = Ui::MakeWeak(this); + const auto controller = _navigation->parentController(); + if (type == u"NO_CHAT_TITLE"_q) { + const auto weak = Ui::MakeWeak(this); selectUsersBox->closeBox(); if (weak) { _title->showError(); } - } else if (error.type() == qstr("USERS_TOO_FEW")) { - Ui::show( + } else if (type == u"USERS_TOO_FEW"_q) { + controller->show( Box<Ui::InformBox>(tr::lng_cant_invite_privacy(tr::now)), Ui::LayerOption::KeepOther); - } else if (error.type() == qstr("PEER_FLOOD")) { - Ui::show( + } else if (type == u"PEER_FLOOD"_q) { + controller->show( Box<Ui::InformBox>( PeerFloodErrorText( &_navigation->session(), PeerFloodType::InviteGroup)), Ui::LayerOption::KeepOther); - } else if (error.type() == qstr("USER_RESTRICTED")) { - Ui::show( + } else if (type == u"USER_RESTRICTED"_q) { + controller->show( Box<Ui::InformBox>(tr::lng_cant_do_this(tr::now)), Ui::LayerOption::KeepOther); } @@ -756,14 +760,20 @@ void GroupInfoBox::createChannel( closeBox(); } }).fail([this](const MTP::Error &error) { + const auto &type = error.type(); _creationRequestId = 0; - if (error.type() == "NO_CHAT_TITLE") { + const auto controller = _navigation->parentController(); + if (type == u"NO_CHAT_TITLE"_q) { _title->setFocus(); _title->showError(); - } else if (error.type() == qstr("USER_RESTRICTED")) { - Ui::show(Box<Ui::InformBox>(tr::lng_cant_do_this(tr::now))); - } else if (error.type() == qstr("CHANNELS_TOO_MUCH")) { - Ui::show(Box<Ui::InformBox>(tr::lng_cant_do_this(tr::now))); // TODO + } else if (type == u"USER_RESTRICTED"_q) { + controller->show( + Box<Ui::InformBox>(tr::lng_cant_do_this(tr::now)), + Ui::LayerOption::CloseOther); + } else if (type == u"CHANNELS_TOO_MUCH"_q) { + controller->show( + Box<Ui::InformBox>(tr::lng_cant_do_this(tr::now)), + Ui::LayerOption::CloseOther); // TODO } }).send(); } @@ -795,9 +805,9 @@ void GroupInfoBox::channelReady() { closeBox(); callback(argument); } else { - Ui::show(Box<SetupChannelBox>( - _navigation, - _createdChannel)); + _navigation->parentController()->show( + Box<SetupChannelBox>(_navigation, _createdChannel), + Ui::LayerOption::CloseOther); } } @@ -890,7 +900,7 @@ void SetupChannelBox::prepare() { MTP_string("preston") )).fail([=](const MTP::Error &error) { _checkRequestId = 0; - firstCheckFail(error.type()); + firstCheckFail(parseError(error.type())); }).send(); addButton(tr::lng_settings_save(), [=] { save(); }); @@ -1119,7 +1129,7 @@ void SetupChannelBox::save() { closeBox(); }).fail([=](const MTP::Error &error) { _saveRequestId = 0; - updateFail(error.type()); + updateFail(parseError(error.type())); }).send(); }; if (_saveRequestId) { @@ -1207,7 +1217,7 @@ void SetupChannelBox::check() { update(); }).fail([=](const MTP::Error &error) { _checkRequestId = 0; - checkFail(error.type()); + checkFail(parseError(error.type())); }).send(); } } @@ -1241,20 +1251,38 @@ void SetupChannelBox::privacyChanged(Privacy value) { update(); } -void SetupChannelBox::updateFail(const QString &error) { - if ((error == "USERNAME_NOT_MODIFIED") +SetupChannelBox::UsernameResult SetupChannelBox::parseError( + const QString &error) { + if (error == u"USERNAME_NOT_MODIFIED"_q) { + return UsernameResult::Ok; + } else if (error == u"USERNAME_INVALID"_q) { + return UsernameResult::Invalid; + } else if (error == u"USERNAME_OCCUPIED"_q) { + return UsernameResult::Occupied; + } else if (error == u"USERNAMES_UNAVAILABLE"_q) { + return UsernameResult::Occupied; + } else if (error == u"CHANNEL_PUBLIC_GROUP_NA"_q) { + return UsernameResult::NA; + } else if (error == u"CHANNELS_ADMIN_PUBLIC_TOO_MUCH"_q) { + return UsernameResult::ChatsTooMuch; + } else { + return UsernameResult::Unknown; + } +} + +void SetupChannelBox::updateFail(UsernameResult result) { + if ((result == UsernameResult::Ok) || (_sentUsername == _channel->username)) { _channel->setName( TextUtilities::SingleLine(_channel->name), TextUtilities::SingleLine(_sentUsername)); closeBox(); - } else if (error == "USERNAME_INVALID") { + } else if (result == UsernameResult::Invalid) { _link->setFocus(); _link->showError(); _errorText = tr::lng_create_channel_link_invalid(tr::now); update(); - } else if ((error == "USERNAME_OCCUPIED") - || (error == "USERNAMES_UNAVAILABLE")) { + } else if (result == UsernameResult::Occupied) { _link->setFocus(); _link->showError(); _errorText = tr::lng_create_channel_link_occupied(tr::now); @@ -1264,20 +1292,20 @@ void SetupChannelBox::updateFail(const QString &error) { } } -void SetupChannelBox::checkFail(const QString &error) { - if (error == qstr("CHANNEL_PUBLIC_GROUP_NA")) { +void SetupChannelBox::checkFail(UsernameResult result) { + if (result == UsernameResult::NA) { Ui::hideLayer(); - } else if (error == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) { + } else if (result == UsernameResult::ChatsTooMuch) { if (_existing) { showRevokePublicLinkBoxForEdit(); } else { _tooMuchUsernames = true; _privacyGroup->setValue(Privacy::Private); } - } else if (error == qstr("USERNAME_INVALID")) { + } else if (result == UsernameResult::Invalid) { _errorText = tr::lng_create_channel_link_invalid(tr::now); update(); - } else if (error == qstr("USERNAME_OCCUPIED") + } else if ((result == UsernameResult::Occupied) && _checkUsername != _channel->username) { _errorText = tr::lng_create_channel_link_occupied(tr::now); update(); @@ -1304,10 +1332,10 @@ void SetupChannelBox::showRevokePublicLinkBoxForEdit() { Ui::LayerOption::KeepOther); } -void SetupChannelBox::firstCheckFail(const QString &error) { - if (error == qstr("CHANNEL_PUBLIC_GROUP_NA")) { +void SetupChannelBox::firstCheckFail(UsernameResult result) { + if (result == UsernameResult::NA) { Ui::hideLayer(); - } else if (error == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) { + } else if (result == UsernameResult::ChatsTooMuch) { if (_existing) { showRevokePublicLinkBoxForEdit(); } else { diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index 0f2d884cc..885ff9d12 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -171,16 +171,26 @@ private: Public, Private, }; + enum class UsernameResult { + Ok, + Invalid, + Occupied, + ChatsTooMuch, + NA, + Unknown, + }; + [[nodiscard]] UsernameResult parseError(const QString &error); + void privacyChanged(Privacy value); void updateSelected(const QPoint &cursorGlobalPosition); void handleChange(); void check(); void save(); - void updateFail(const QString &error); + void updateFail(UsernameResult result); - void checkFail(const QString &error); - void firstCheckFail(const QString &error); + void checkFail(UsernameResult result); + void firstCheckFail(UsernameResult result); void updateMaxHeight(); From d218b76efe5f71f95bdb59c951fc3e58cdaa24f6 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 28 Nov 2021 07:51:34 +0300 Subject: [PATCH 145/180] Removed MTP::Error from migrating chat. --- Telegram/SourceFiles/apiwrap.cpp | 17 ++++++++--------- Telegram/SourceFiles/apiwrap.h | 6 +++--- .../boxes/peers/edit_peer_info_box.cpp | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index cf4c75129..388deb764 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1024,7 +1024,7 @@ void ApiWrap::requestFullPeer(not_null<PeerData*> peer) { const auto requestId = [&] { const auto failHandler = [=](const MTP::Error &error) { _fullPeerRequests.remove(peer); - migrateFail(peer, error); + migrateFail(peer, error.type()); }; if (const auto user = peer->asUser()) { if (_session->supportMode()) { @@ -1191,7 +1191,7 @@ void ApiWrap::requestPeerSettings(not_null<PeerData*> peer) { void ApiWrap::migrateChat( not_null<ChatData*> chat, FnMut<void(not_null<ChannelData*>)> done, - Fn<void(const MTP::Error &)> fail) { + Fn<void(const QString &)> fail) { const auto callback = [&] { return MigrateCallbacks{ std::move(done), std::move(fail) }; }; @@ -1214,7 +1214,7 @@ void ApiWrap::migrateChat( chat, MTP::Error::Local( "BAD_MIGRATION", - "Chat is already deactivated")); + "Chat is already deactivated").type()); }); return; } else if (!chat->amCreator()) { @@ -1223,7 +1223,7 @@ void ApiWrap::migrateChat( chat, MTP::Error::Local( "BAD_MIGRATION", - "Current user is not the creator of that chat")); + "Current user is not the creator of that chat").type()); }); return; } @@ -1242,10 +1242,10 @@ void ApiWrap::migrateChat( } else { migrateFail( chat, - MTP::Error::Local("MIGRATION_FAIL", "No channel")); + MTP::Error::Local("MIGRATION_FAIL", "No channel").type()); } }).fail([=](const MTP::Error &error) { - migrateFail(chat, error); + migrateFail(chat, error.type()); }).send(); } @@ -1262,9 +1262,8 @@ void ApiWrap::migrateDone( } } -void ApiWrap::migrateFail(not_null<PeerData*> peer, const MTP::Error &error) { - const auto &type = error.type(); - if (type == qstr("CHANNELS_TOO_MUCH")) { +void ApiWrap::migrateFail(not_null<PeerData*> peer, const QString &error) { + if (error == u"CHANNELS_TOO_MUCH"_q) { Ui::show(Box<Ui::InformBox>(tr::lng_migrate_error(tr::now))); } if (auto handlers = _migrateCallbacks.take(peer)) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 80d109eff..52a325766 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -208,7 +208,7 @@ public: void migrateChat( not_null<ChatData*> chat, FnMut<void(not_null<ChannelData*>)> done, - Fn<void(const MTP::Error &)> fail = nullptr); + Fn<void(const QString &)> fail = nullptr); void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items); void markMediaRead(not_null<HistoryItem*> item); @@ -512,7 +512,7 @@ private: void migrateDone( not_null<PeerData*> peer, not_null<ChannelData*> channel); - void migrateFail(not_null<PeerData*> peer, const MTP::Error &error); + void migrateFail(not_null<PeerData*> peer, const QString &error); not_null<Main::Session*> _session; @@ -613,7 +613,7 @@ private: struct MigrateCallbacks { FnMut<void(not_null<ChannelData*>)> done; - Fn<void(const MTP::Error&)> fail; + Fn<void(const QString&)> fail; }; base::flat_map< not_null<PeerData*>, diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index c9fca8adf..859504da4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp @@ -242,7 +242,7 @@ void ShowEditPermissions( const auto api = &peer->session().api(); api->migrateChat(chat, [=](not_null<ChannelData*> channel) { save(channel, result); - }, [=](const MTP::Error &) { + }, [=](const QString &) { *saving = false; }); }, box->lifetime()); From 77d1d9bd3a3223d84ea06305a1ac7b36cbf25859 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 28 Nov 2021 11:25:25 +0300 Subject: [PATCH 146/180] Moved out getting of phone pattern groups from PhoneInput fields. --- .../SourceFiles/boxes/add_contact_box.cpp | 3 ++- .../SourceFiles/boxes/change_phone_box.cpp | 3 ++- .../countries/countries_instance.cpp | 8 ++++++ .../countries/countries_instance.h | 1 + Telegram/SourceFiles/intro/intro_phone.cpp | 6 ++++- Telegram/SourceFiles/intro/intro_qr.h | 7 ----- .../passport/passport_panel_edit_contact.cpp | 3 ++- .../payments/ui/payments_field.cpp | 3 ++- Telegram/SourceFiles/ui/special_fields.cpp | 26 +++++++++---------- Telegram/SourceFiles/ui/special_fields.h | 15 +++++++++-- 10 files changed, 47 insertions(+), 28 deletions(-) diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index d0b63284f..43fa0abb4 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -261,7 +261,8 @@ AddContactBox::AddContactBox( st::defaultInputField, tr::lng_contact_phone(), Countries::ExtractPhoneCode(session->user()->phone()), - phone) + phone, + [](const QString &s) { return Countries::Groups(s); }) , _invertOrder(langFirstNameGoesSecond()) { if (!phone.isEmpty()) { _phone->setDisabled(true); diff --git a/Telegram/SourceFiles/boxes/change_phone_box.cpp b/Telegram/SourceFiles/boxes/change_phone_box.cpp index dc8194cbe..2a589ddde 100644 --- a/Telegram/SourceFiles/boxes/change_phone_box.cpp +++ b/Telegram/SourceFiles/boxes/change_phone_box.cpp @@ -156,7 +156,8 @@ void ChangePhoneBox::EnterPhone::prepare() { st::defaultInputField, tr::lng_change_phone_new_title(), Countries::ExtractPhoneCode(_controller->session().user()->phone()), - phoneValue); + phoneValue, + [](const QString &s) { return Countries::Groups(s); }); _phone->resize( st::boxWidth - 2 * st::boxPadding.left(), diff --git a/Telegram/SourceFiles/countries/countries_instance.cpp b/Telegram/SourceFiles/countries/countries_instance.cpp index 8e6aaf785..88c42c213 100644 --- a/Telegram/SourceFiles/countries/countries_instance.cpp +++ b/Telegram/SourceFiles/countries/countries_instance.cpp @@ -470,4 +470,12 @@ QString ExtractPhoneCode(const QString &phone) { return Instance().format({ .phone = phone, .onlyCode = true }).code; } +QVector<int> Groups(const QString &phone) { + return Instance().format({ + .phone = phone, + .onlyGroups = true, + .incomplete = true, + }).groups; +} + } // namespace Countries diff --git a/Telegram/SourceFiles/countries/countries_instance.h b/Telegram/SourceFiles/countries/countries_instance.h index 6052377b2..d8d7562e8 100644 --- a/Telegram/SourceFiles/countries/countries_instance.h +++ b/Telegram/SourceFiles/countries/countries_instance.h @@ -70,5 +70,6 @@ private: CountriesInstance &Instance(); [[nodiscard]] QString ExtractPhoneCode(const QString &phone); +[[nodiscard]] QVector<int> Groups(const QString &phone); } // namespace Countries diff --git a/Telegram/SourceFiles/intro/intro_phone.cpp b/Telegram/SourceFiles/intro/intro_phone.cpp index 8237f2e9b..adb9c55a7 100644 --- a/Telegram/SourceFiles/intro/intro_phone.cpp +++ b/Telegram/SourceFiles/intro/intro_phone.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "boxes/phone_banned_box.h" #include "core/application.h" +#include "countries/countries_instance.h" // Countries::Groups namespace Intro { namespace details { @@ -44,7 +45,10 @@ PhoneWidget::PhoneWidget( : Step(parent, account, data) , _country(this, st::introCountry) , _code(this, st::introCountryCode) -, _phone(this, st::introPhone) +, _phone( + this, + st::introPhone, + [](const QString &s) { return Countries::Groups(s); }) , _checkRequestTimer([=] { checkRequest(); }) { _phone->frontBackspaceEvent( ) | rpl::start_with_next([=](not_null<QKeyEvent*> e) { diff --git a/Telegram/SourceFiles/intro/intro_qr.h b/Telegram/SourceFiles/intro/intro_qr.h index 99d19a316..7cc166a4b 100644 --- a/Telegram/SourceFiles/intro/intro_qr.h +++ b/Telegram/SourceFiles/intro/intro_qr.h @@ -11,13 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "intro/intro_step.h" #include "base/timer.h" -namespace Ui { -class PhonePartInput; -class CountryCodeInput; -class RoundButton; -class FlatLabel; -} // namespace Ui - namespace Intro { namespace details { diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp index c0505fe49..b9e3246b0 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_contact.cpp @@ -281,7 +281,8 @@ void PanelEditContact::setupControls( std::move(fieldPlaceholder), Countries::ExtractPhoneCode( _controller->bot()->session().user()->phone()), - data); + data, + [](const QString &s) { return Countries::Groups(s); }); } else { _field = Ui::CreateChild<Ui::MaskedInputField>( wrap.data(), diff --git a/Telegram/SourceFiles/payments/ui/payments_field.cpp b/Telegram/SourceFiles/payments/ui/payments_field.cpp index 522a7aa3b..6c90059d3 100644 --- a/Telegram/SourceFiles/payments/ui/payments_field.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_field.cpp @@ -407,7 +407,8 @@ struct SimpleFieldState { st::paymentsField, std::move(config.placeholder), Countries::ExtractPhoneCode(config.defaultPhone), - Parse(config)); + Parse(config), + [](const QString &s) { return Countries::Groups(s); }); case FieldType::Money: return CreateMoneyField( wrap, diff --git a/Telegram/SourceFiles/ui/special_fields.cpp b/Telegram/SourceFiles/ui/special_fields.cpp index 61cb4198a..8a741b5df 100644 --- a/Telegram/SourceFiles/ui/special_fields.cpp +++ b/Telegram/SourceFiles/ui/special_fields.cpp @@ -96,8 +96,12 @@ void CountryCodeInput::correctValue( } } -PhonePartInput::PhonePartInput(QWidget *parent, const style::InputField &st) -: MaskedInputField(parent, st/*, tr::lng_phone_ph(tr::now)*/) { +PhonePartInput::PhonePartInput( + QWidget *parent, + const style::InputField &st, + PhonePartInput::GroupsCallback groupsCallback) +: MaskedInputField(parent, st/*, tr::lng_phone_ph(tr::now)*/) +, _groupsCallback(std::move(groupsCallback)) { } void PhonePartInput::paintAdditionalPlaceholder(Painter &p) { @@ -208,11 +212,7 @@ void PhonePartInput::addedToNumber(const QString &added) { } void PhonePartInput::chooseCode(const QString &code) { - _pattern = Countries::Instance().format({ - .phone = code, - .onlyGroups = true, - .incomplete = true, - }).groups; + _pattern = _groupsCallback(code); if (!_pattern.isEmpty() && _pattern.at(0) == code.size()) { _pattern.pop_front(); } else { @@ -297,9 +297,11 @@ PhoneInput::PhoneInput( const style::InputField &st, rpl::producer<QString> placeholder, const QString &defaultValue, - QString value) + QString value, + PhoneInput::GroupsCallback groupsCallback) : MaskedInputField(parent, st, std::move(placeholder), value) -, _defaultValue(defaultValue) { +, _defaultValue(defaultValue) +, _groupsCallback(std::move(groupsCallback)) { if (value.isEmpty()) { clearText(); } else { @@ -344,11 +346,7 @@ void PhoneInput::correctValue( int &nowCursor) { auto digits = now; digits.replace(QRegularExpression("[^\\d]"), QString()); - _pattern = Countries::Instance().format({ - .phone = digits, - .onlyGroups = true, - .incomplete = true, - }).groups; + _pattern = _groupsCallback(digits); QString newPlaceholder; if (_pattern.isEmpty()) { diff --git a/Telegram/SourceFiles/ui/special_fields.h b/Telegram/SourceFiles/ui/special_fields.h index 39be5c162..78327d87e 100644 --- a/Telegram/SourceFiles/ui/special_fields.h +++ b/Telegram/SourceFiles/ui/special_fields.h @@ -42,7 +42,12 @@ private: class PhonePartInput : public MaskedInputField { public: - PhonePartInput(QWidget *parent, const style::InputField &st); + using GroupsCallback = Fn<QVector<int>(const QString &)>; + + PhonePartInput( + QWidget *parent, + const style::InputField &st, + GroupsCallback groupsCallback); [[nodiscard]] auto frontBackspaceEvent() const -> rpl::producer<not_null<QKeyEvent*>> { @@ -66,6 +71,7 @@ private: QVector<int> _pattern; QString _additionalPlaceholder; rpl::event_stream<not_null<QKeyEvent*>> _frontBackspaceEvent; + GroupsCallback _groupsCallback; }; @@ -95,12 +101,15 @@ private: class PhoneInput : public MaskedInputField { public: + using GroupsCallback = Fn<QVector<int>(const QString &)>; + PhoneInput( QWidget *parent, const style::InputField &st, rpl::producer<QString> placeholder, const QString &defaultValue, - QString value); + QString value, + GroupsCallback groupsCallback); void clearText(); @@ -119,6 +128,8 @@ private: QVector<int> _pattern; QString _additionalPlaceholder; + GroupsCallback _groupsCallback; + }; } // namespace Ui From 9706b84409c5c92152c76f5400de0f4a9399f105 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 28 Nov 2021 23:16:53 +0300 Subject: [PATCH 147/180] Slightly refactored api_bot. --- Telegram/SourceFiles/api/api_bot.cpp | 77 +++++++++++++++------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/Telegram/SourceFiles/api/api_bot.cpp b/Telegram/SourceFiles/api/api_bot.cpp index 5c463ab44..4bf5b79fb 100644 --- a/Telegram/SourceFiles/api/api_bot.cpp +++ b/Telegram/SourceFiles/api/api_bot.cpp @@ -34,8 +34,8 @@ void SendBotCallbackData( not_null<HistoryItem*> item, int row, int column, - std::optional<MTPInputCheckPasswordSRP> password = std::nullopt, - Fn<void(const MTP::Error &)> handleError = nullptr) { + std::optional<Core::CloudPasswordResult> password = std::nullopt, + Fn<void(const QString &)> handleError = nullptr) { if (!item->isRegular()) { return; } @@ -78,7 +78,7 @@ void SendBotCallbackData( history->peer->input, MTP_int(item->id), MTP_bytes(sendData), - password.value_or(MTP_inputCheckPasswordEmpty()) + password ? password->result : MTP_inputCheckPasswordEmpty() )).done([=](const MTPmessages_BotCallbackAnswer &result) { const auto item = owner->message(fullId); if (!item) { @@ -88,34 +88,41 @@ void SendBotCallbackData( button->requestId = 0; owner->requestItemRepaint(item); } - result.match([&](const MTPDmessages_botCallbackAnswer &data) { - if (const auto message = data.vmessage()) { - if (data.is_alert()) { - Ui::show(Box<Ui::InformBox>(qs(*message))); - } else { - if (withPassword) { - Ui::hideLayer(); - } - Ui::Toast::Show(qs(*message)); - } - } else if (const auto url = data.vurl()) { - const auto link = qs(*url); - if (!isGame) { - UrlClickHandler::Open(link); - return; - } - const auto scoreLink = AppendShareGameScoreUrl( - session, - link, - item->fullId()); - BotGameUrlClickHandler(bot, scoreLink).onClick({}); - session->sendProgressManager().update( - history, - Api::SendProgressType::PlayGame); - } else if (withPassword) { - Ui::hideLayer(); - } + const auto &data = result.match([]( + const auto &data) -> const MTPDmessages_botCallbackAnswer& { + return data; }); + const auto message = data.vmessage() + ? qs(*data.vmessage()) + : QString(); + const auto link = data.vurl() ? qs(*data.vurl()) : QString(); + const auto showAlert = data.is_alert(); + + if (!message.isEmpty()) { + if (showAlert) { + Ui::show(Box<Ui::InformBox>(message)); + } else { + if (withPassword) { + Ui::hideLayer(); + } + Ui::Toast::Show(message); + } + } else if (!link.isEmpty()) { + if (!isGame) { + UrlClickHandler::Open(link); + return; + } + const auto scoreLink = AppendShareGameScoreUrl( + session, + link, + item->fullId()); + BotGameUrlClickHandler(bot, scoreLink).onClick({}); + session->sendProgressManager().update( + history, + Api::SendProgressType::PlayGame); + } else if (withPassword) { + Ui::hideLayer(); + } }).fail([=](const MTP::Error &error) { const auto item = owner->message(fullId); if (!item) { @@ -127,7 +134,7 @@ void SendBotCallbackData( owner->requestItemRepaint(item); } if (handleError) { - handleError(error); + handleError(error.type()); } }).send(); @@ -143,7 +150,7 @@ void SendBotCallbackData( not_null<HistoryItem*> item, int row, int column) { - SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty()); + SendBotCallbackData(item, row, column, std::nullopt); } void SendBotCallbackDataWithPassword( @@ -170,9 +177,9 @@ void SendBotCallbackDataWithPassword( return; } api->cloudPassword().reload(); - SendBotCallbackData(item, row, column, MTP_inputCheckPasswordEmpty(), [=](const MTP::Error &error) { + SendBotCallbackData(item, row, column, std::nullopt, [=](const QString &error) { auto box = PrePasswordErrorBox( - error.type(), + error, session, tr::lng_bots_password_confirm_check_about( tr::now, @@ -212,7 +219,7 @@ void SendBotCallbackDataWithPassword( return; } if (const auto item = owner->message(fullId)) { - SendBotCallbackData(item, row, column, result.result, [=](const MTP::Error &error) { + SendBotCallbackData(item, row, column, result, [=](const QString &error) { if (*box) { (*box)->handleCustomCheckError(error); } From fa32151391650fd1d26e52863a0105c9627ae1d4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 17:56:46 +0400 Subject: [PATCH 148/180] Add distinct strings for groups / channels noforwards. --- Telegram/Resources/langs/lang.strings | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ab5fd1b85..0ea9630f1 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1020,7 +1020,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_public_peer_title" = "Public"; "lng_manage_peer_no_forwards_title" = "Saving content"; "lng_manage_peer_no_forwards" = "Restrict saving content"; -"lng_manage_peer_no_forwards_about" = "Participants won't be able to forward messages from this channel or save media files."; +"lng_manage_peer_no_forwards_about" = "Members won't be able to forward messages from this group or save media files."; +"lng_manage_peer_no_forwards_about_channel" = "Subscribers won't be able to forward messages from this channel or save media files."; "lng_manage_discussion_group" = "Discussion"; "lng_manage_discussion_group_add" = "Add a group"; From 899de8a35936729802b408df13578c0e082cebcf Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 30 Nov 2021 17:30:53 +0400 Subject: [PATCH 149/180] Use default values for daysUntilClose and closeComment arguments of no-response bot The current closeComment follows the default comment anyway and it seems the default value of daysUntilClose (14 days) would be fine --- .github/workflows/no-response.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml index 4b201b453..4d19e1048 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/no-response.yml @@ -15,14 +15,5 @@ jobs: - uses: lee-dohm/no-response@v0.5.0 with: token: ${{ github.token }} - # Number of days of inactivity before an Issue is closed for lack of response - daysUntilClose: 30 # Label requiring a response responseRequiredLabel: waiting for answer - # Comment to post when closing an Issue for lack of response. Set to `false` to disable - closeComment: > - This issue has been automatically closed because there has been no response - to our request for more information from the original author. With only the - information that is currently in the issue, we don't have enough information - to take action. Please reach out if you have or find the answers we need so - that we can investigate further. From 0f0ade54a7449dae0a2369263c0bf9d1690acd65 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 30 Nov 2021 17:33:01 +0400 Subject: [PATCH 150/180] Update lock-threads bot to v3 --- .github/workflows/lock.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 11e0eef67..562159d0d 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -8,8 +8,8 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v2 + - uses: dessant/lock-threads@v3 with: github-token: ${{ github.token }} - issue-lock-inactive-days: 45 - pr-lock-inactive-days: 45 + issue-inactive-days: 45 + pr-inactive-days: 45 From 40330afbd30863b638b5be8a4597c1b5dc5c6468 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 18:07:16 +0400 Subject: [PATCH 151/180] Use distinct strings for groups / channels noforwards. --- Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 44d9e5741..36ccef59a 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -224,7 +224,11 @@ void Controller::createContent() { _noForwardsSavedValue = toggled; }, _wrap->lifetime()); AddSkip(_wrap.get()); - AddDividerText(_wrap.get(), tr::lng_manage_peer_no_forwards_about()); + AddDividerText( + _wrap.get(), + (_isGroup + ? tr::lng_manage_peer_no_forwards_about + : tr::lng_manage_peer_no_forwards_about_channel)()); } if (_linkOnly) { _controls.inviteLinkWrap->show(anim::type::instant); From 7bf557971e03ec101cb1d813a93828a1a421fbf4 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 20:19:23 +0400 Subject: [PATCH 152/180] Fix "Send As" button on Retina screens. --- Telegram/SourceFiles/ui/controls/send_as_button.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/ui/controls/send_as_button.cpp b/Telegram/SourceFiles/ui/controls/send_as_button.cpp index b9a124176..062350541 100644 --- a/Telegram/SourceFiles/ui/controls/send_as_button.cpp +++ b/Telegram/SourceFiles/ui/controls/send_as_button.cpp @@ -18,7 +18,6 @@ SendAsButton::SendAsButton(QWidget *parent, const style::SendAsButton &st) resize(_st.width, _st.height); } - void SendAsButton::setUserpic(QImage userpic) { _userpic = std::move(userpic); update(); @@ -44,7 +43,7 @@ void SendAsButton::paintEvent(QPaintEvent *e) { const auto active = _activeAnimation.value(_active ? 1. : 0.); if (active < 1. && !_userpic.isNull()) { - p.drawImage(left, top, _userpic); + p.drawImage(QRect(left, top, _st.size, _st.size), _userpic); } if (active > 0.) { p.setOpacity(active); From 168711b352c31357cd93118fa78aa4bb05433081 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 20:38:47 +0400 Subject: [PATCH 153/180] Use PeerList for sessions list (wip). --- Telegram/SourceFiles/boxes/sessions_box.cpp | 499 +++++++++++-------- Telegram/SourceFiles/settings/settings.style | 47 +- 2 files changed, 316 insertions(+), 230 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 501fdd428..630fcb304 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/algorithm.h" #include "base/platform/base_platform_info.h" #include "boxes/self_destruction_box.h" +#include "boxes/peer_lists_box.h" #include "ui/boxes/confirm_box.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -57,6 +58,50 @@ enum class Type { Other, }; +class Row; + +class RowDelegate { +public: + virtual void rowUpdateRow(not_null<Row*> row) = 0; +}; + +class Row final : public PeerListRow { +public: + Row(not_null<RowDelegate*> delegate, const EntryData &data); + + void update(const EntryData &data); + void updateName(const QString &name); + + [[nodiscard]] EntryData data() const; + + QString generateName() override; + QString generateShortName() override; + PaintRoundImageCallback generatePaintUserpicCallback() override; + + int elementsCount() const override; + QRect elementGeometry(int element, int outerWidth) const override; + bool elementDisabled(int element) const override; + bool elementOnlySelect(int element) const override; + void elementAddRipple( + int element, + QPoint point, + Fn<void()> updateCallback) override; + void elementsStopLastRipple() override; + void elementsPaint( + Painter &p, + int outerWidth, + bool selected, + int selectedElement) override; + +private: + const not_null<RowDelegate*> _delegate; + Ui::Text::String _location; + Type _type = Type::Other; + EntryData _data; + QImage _userpic; + +}; + void RenameBox(not_null<Ui::GenericBox*> box) { box->setTitle(tr::lng_settings_rename_device_title()); @@ -255,7 +300,7 @@ void RenameBox(not_null<Ui::GenericBox*> box) { } [[nodiscard]] QImage GenerateUserpic(Type type) { - const auto size = st::sessionUserpicSize; + const auto size = st::sessionListItem.photoSize; const auto full = size * style::DevicePixelRatio(); const auto rect = QRect(0, 0, size, size); @@ -440,6 +485,125 @@ void SessionInfoBox( } } +Row::Row(not_null<RowDelegate*> delegate, const EntryData &data) +: PeerListRow(data.hash) +, _delegate(delegate) +, _location(st::defaultTextStyle, LocationAndDate(data)) +, _type(TypeFromEntry(data)) +, _data(data) +, _userpic(GenerateUserpic(_type)) { + setCustomStatus(_data.info); +} + +void Row::update(const EntryData &data) { + _data = data; + setCustomStatus(_data.info); + refreshName(st::sessionListItem); + _location.setText(st::defaultTextStyle, LocationAndDate(_data)); + _delegate->rowUpdateRow(this); +} + +void Row::updateName(const QString &name) { + _data.name = name; + refreshName(st::sessionListItem); + _delegate->rowUpdateRow(this); +} + +EntryData Row::data() const { + return _data; +} + +QString Row::generateName() { + return _data.name; +} + +QString Row::generateShortName() { + return generateName(); +} + +PaintRoundImageCallback Row::generatePaintUserpicCallback() { + return [=]( + Painter &p, + int x, + int y, + int outerWidth, + int size) { + p.drawImage(x, y, _userpic); + }; +} + +int Row::elementsCount() const { + return 2; +} + +QRect Row::elementGeometry(int element, int outerWidth) const { + switch (element) { + case 1: { + return QRect( + st::sessionListItem.namePosition.x(), + st::sessionLocationTop, + outerWidth, + st::normalFont->height); + } break; + case 2: { + const auto size = QSize( + st::sessionListThreeDotsIcon.width(), + st::sessionListThreeDotsIcon.height()); + const auto margins = QMargins( + 0, + (st::sessionListItem.height - size.height()) / 2, + st::sessionListThreeDotsSkip, + 0); + const auto right = margins.right(); + const auto top = margins.top(); + const auto left = outerWidth - right - size.width(); + return QRect(QPoint(left, top), size); + } break; + } + return QRect(); +} + +bool Row::elementDisabled(int element) const { + return !id() || (element == 1); +} + +bool Row::elementOnlySelect(int element) const { + return false; +} + +void Row::elementAddRipple( + int element, + QPoint point, + Fn<void()> updateCallback) { +} + +void Row::elementsStopLastRipple() { +} + +void Row::elementsPaint( + Painter &p, + int outerWidth, + bool selected, + int selectedElement) { + if (id()) { + const auto geometry = elementGeometry(2, outerWidth); + const auto &icon = (selectedElement == 2) + ? st::sessionListThreeDotsIconOver + : st::sessionListThreeDotsIcon; + icon.paint(p, geometry.x(), geometry.y(), outerWidth); + } + p.setFont(st::msgFont); + p.setPen(st::sessionInfoFg); + const auto locationLeft = st::sessionListItem.namePosition.x(); + const auto available = outerWidth - locationLeft; + _location.drawLeftElided( + p, + locationLeft, + st::sessionLocationTop, + available, + outerWidth); +} + } // namespace class SessionsContent : public Ui::RpWidget { @@ -455,25 +619,13 @@ protected: void paintEvent(QPaintEvent *e) override; private: - struct Entry { - Entry() = default; - explicit Entry(const EntryData &entry); - - EntryData data; - - bool incomplete = false; - Type type = Type::Other; - TimeId activeTime = 0; - Ui::Text::String name, info, location; - QImage userpic; - }; struct Full { - Entry current; - std::vector<Entry> incomplete; - std::vector<Entry> list; + EntryData current; + std::vector<EntryData> incomplete; + std::vector<EntryData> list; }; class Inner; - class List; + class ListController; void shortPollSessions(); void parse(const Api::Authorizations::List &list); @@ -495,44 +647,39 @@ private: }; -class SessionsContent::List : public Ui::RpWidget { +class SessionsContent::ListController final + : public PeerListController + , public RowDelegate + , public base::has_weak_ptr { public: - List(QWidget *parent); + explicit ListController(not_null<Main::Session*> session); - void showData(gsl::span<const Entry> items); + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null<PeerListRow*> row) override; + void rowElementClicked(not_null<PeerListRow*> row, int element) override; + + void rowUpdateRow(not_null<Row*> row) override; + + void showData(gsl::span<const EntryData> items); rpl::producer<int> itemsCount() const; rpl::producer<uint64> terminateRequests() const; [[nodiscard]] rpl::producer<EntryData> showRequests() const; - void terminating(uint64 hash, bool terminating); - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - - int resizeGetHeight(int newWidth) override; + [[nodiscard]] static std::unique_ptr<ListController> Add( + not_null<Ui::VerticalLayout*> container, + not_null<Main::Session*> session, + style::margins margins = {}); private: - struct RowWidth { - int available = 0; - int info = 0; - }; - void subscribeToCustomDeviceModel(); - void computeRowWidth(); - RowWidth _rowWidth; - std::vector<Entry> _items; - std::map<uint64, std::unique_ptr<Ui::IconButton>> _terminateButtons; + const not_null<Main::Session*> _session; + rpl::event_stream<uint64> _terminateRequests; rpl::event_stream<int> _itemsCount; rpl::event_stream<EntryData> _showRequests; - int _pressed = -1; - }; class SessionsContent::Inner : public Ui::RpWidget { @@ -546,32 +693,20 @@ public: [[nodiscard]] rpl::producer<EntryData> showRequests() const; [[nodiscard]] rpl::producer<uint64> terminateOne() const; [[nodiscard]] rpl::producer<> terminateAll() const; - [[nodiscard]] rpl::producer<> renameCurrentRequests() const; - - void terminatingOne(uint64 hash, bool terminating); private: void setupContent(); const not_null<Window::SessionController*> _controller; - QPointer<List> _current; + std::unique_ptr<ListController> _current; QPointer<Ui::SettingsButton> _terminateAll; - QPointer<List> _incomplete; - QPointer<List> _list; + std::unique_ptr<ListController> _incomplete; + std::unique_ptr<ListController> _list; rpl::variable<int> _ttlDays; }; -SessionsContent::Entry::Entry(const EntryData &entry) -: data(entry) -, incomplete(entry.incomplete) -, type(TypeFromEntry(entry)) -, activeTime(entry.activeTime) -, name(st::sessionNameStyle, entry.name) -, info(st::sessionInfoStyle, entry.info) -, location(st::sessionInfoStyle, LocationAndDate(entry)) -, userpic(GenerateUserpic(type)) { -}; +//, location(st::sessionInfoStyle, LocationAndDate(entry)) SessionsContent::SessionsContent( QWidget*, @@ -629,20 +764,19 @@ void SessionsContent::parse(const Api::Authorizations::List &list) { } _data = Full(); for (const auto &auth : list) { - auto entry = Entry(auth); - if (!entry.data.hash) { - _data.current = std::move(entry); - } else if (entry.incomplete) { - _data.incomplete.push_back(std::move(entry)); + if (!auth.hash) { + _data.current = auth; + } else if (auth.incomplete) { + _data.incomplete.push_back(auth); } else { - _data.list.push_back(std::move(entry)); + _data.list.push_back(auth); } } _loading = false; - ranges::sort(_data.list, std::greater<>(), &Entry::activeTime); - ranges::sort(_data.incomplete, std::greater<>(), &Entry::activeTime); + ranges::sort(_data.list, std::greater<>(), &EntryData::activeTime); + ranges::sort(_data.incomplete, std::greater<>(), &EntryData::activeTime); _inner->showData(_data); @@ -710,13 +844,12 @@ void SessionsContent::terminateOne(uint64 hash) { if (mtpIsFalse(result)) { return; } - _inner->terminatingOne(hash, false); - const auto removeByHash = [&](std::vector<Entry> &list) { + const auto removeByHash = [&](std::vector<EntryData> &list) { list.erase( ranges::remove( list, hash, - [](const Entry &entry) { return entry.data.hash; }), + [](const EntryData &entry) { return entry.hash; }), end(list)); }; removeByHash(_data.incomplete); @@ -724,13 +857,11 @@ void SessionsContent::terminateOne(uint64 hash) { _inner->showData(_data); }); auto fail = crl::guard(weak, [=](const MTP::Error &error) { - _inner->terminatingOne(hash, false); }); _authorizations->requestTerminate( std::move(done), std::move(fail), hash); - _inner->terminatingOne(hash, true); }; terminate(std::move(callback), tr::lng_settings_reset_one_sure(tr::now)); } @@ -788,8 +919,10 @@ void SessionsContent::Inner::setupContent() { Ui::show(Box(RenameBox), Ui::LayerOption::KeepOther); }); - _current = content->add( - object_ptr<List>(content), + const auto session = &_controller->session(); + _current = ListController::Add( + content, + session, style::margins{ 0, 0, 0, st::sessionCurrentSkip }); const auto terminateWrap = content->add( object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( @@ -814,7 +947,7 @@ void SessionsContent::Inner::setupContent() { const auto incompleteInner = incompleteWrap->entity(); AddSkip(incompleteInner, st::sessionSubtitleSkip); AddSubsectionTitle(incompleteInner, tr::lng_sessions_incomplete()); - _incomplete = incompleteInner->add(object_ptr<List>(incompleteInner)); + _incomplete = ListController::Add(incompleteInner, session); AddSkip(incompleteInner); AddDividerText(incompleteInner, tr::lng_sessions_incomplete_about()); @@ -825,7 +958,7 @@ void SessionsContent::Inner::setupContent() { const auto listInner = listWrap->entity(); AddSkip(listInner, st::sessionSubtitleSkip); AddSubsectionTitle(listInner, tr::lng_sessions_other_header()); - _list = listInner->add(object_ptr<List>(listInner)); + _list = ListController::Add(listInner, session); AddSkip(listInner); AddDividerText(listInner, tr::lng_sessions_about_apps()); @@ -897,165 +1030,113 @@ rpl::producer<EntryData> SessionsContent::Inner::showRequests() const { _list->showRequests()); } -void SessionsContent::Inner::terminatingOne(uint64 hash, bool terminating) { - _incomplete->terminating(hash, terminating); - _list->terminating(hash, terminating); +SessionsContent::ListController::ListController( + not_null<Main::Session*> session) +: _session(session) { } -SessionsContent::List::List(QWidget *parent) : RpWidget(parent) { - setAttribute(Qt::WA_OpaquePaintEvent); +Main::Session &SessionsContent::ListController::session() const { + return *_session; } -void SessionsContent::List::resizeEvent(QResizeEvent *e) { - RpWidget::resizeEvent(e); - - computeRowWidth(); -} - -void SessionsContent::List::subscribeToCustomDeviceModel() { +void SessionsContent::ListController::subscribeToCustomDeviceModel() { Core::App().settings().deviceModelChanges( ) | rpl::start_with_next([=](const QString &model) { - for (auto &entry : _items) { - if (!entry.data.hash) { - entry.name.setText(st::sessionNameStyle, model); + for (auto i = 0; i != delegate()->peerListFullRowsCount(); ++i) { + const auto row = delegate()->peerListRowAt(i); + if (!row->id()) { + static_cast<Row*>(row.get())->updateName(model); } } - update(); }, lifetime()); } -void SessionsContent::List::showData(gsl::span<const Entry> items) { - computeRowWidth(); +void SessionsContent::ListController::prepare() { +} - auto buttons = base::take(_terminateButtons); - _items.clear(); - _items.insert(begin(_items), items.begin(), items.end()); - for (const auto &entry : _items) { - const auto hash = entry.data.hash; - if (!hash) { +void SessionsContent::ListController::rowClicked( + not_null<PeerListRow*> row) { + _showRequests.fire_copy(static_cast<Row*>(row.get())->data()); +} + +void SessionsContent::ListController::rowElementClicked( + not_null<PeerListRow*> row, + int element) { + if (element == 1) { + if (const auto hash = static_cast<Row*>(row.get())->data().hash) { + _terminateRequests.fire_copy(hash); + } + } +} + +void SessionsContent::ListController::rowUpdateRow(not_null<Row*> row) { + delegate()->peerListUpdateRow(row); +} + +void SessionsContent::ListController::showData( + gsl::span<const EntryData> items) { + auto index = 0; + auto positions = base::flat_map<uint64, int>(); + positions.reserve(items.size()); + for (const auto &entry : items) { + const auto id = entry.hash; + positions.emplace(id, index++); + if (const auto row = delegate()->peerListFindRow(id)) { + static_cast<Row*>(row)->update(entry); + } else { + delegate()->peerListAppendRow( + std::make_unique<Row>(this, entry)); + } + } + for (auto i = 0; i != delegate()->peerListFullRowsCount();) { + const auto row = delegate()->peerListRowAt(i); + if (positions.contains(row->id())) { + ++i; continue; } - const auto button = [&] { - const auto i = buttons.find(hash); - return _terminateButtons.emplace( - hash, - (i != end(buttons) - ? std::move(i->second) - : std::make_unique<Ui::IconButton>( - this, - st::sessionTerminate))).first->second.get(); - }(); - button->setClickedCallback([=] { - _terminateRequests.fire_copy(hash); - }); - button->show(); - const auto number = _terminateButtons.size() - 1; - widthValue( - ) | rpl::start_with_next([=] { - button->moveToRight( - st::sessionTerminateSkip, - (number * st::sessionHeight + st::sessionTerminateTop)); - }, lifetime()); + delegate()->peerListRemoveRow(row); } - resizeToWidth(width()); - _itemsCount.fire(_items.size()); + delegate()->peerListSortRows([&]( + const PeerListRow &a, + const PeerListRow &b) { + return positions[a.id()] < positions[b.id()]; + }); + delegate()->peerListRefreshRows(); + _itemsCount.fire(delegate()->peerListFullRowsCount()); } -rpl::producer<EntryData> SessionsContent::List::showRequests() const { - return _showRequests.events(); +rpl::producer<int> SessionsContent::ListController::itemsCount() const { + return _itemsCount.events_starting_with( + delegate()->peerListFullRowsCount()); } -rpl::producer<int> SessionsContent::List::itemsCount() const { - return _itemsCount.events_starting_with(_items.size()); -} - -rpl::producer<uint64> SessionsContent::List::terminateRequests() const { +rpl::producer<uint64> SessionsContent::ListController::terminateRequests() const { return _terminateRequests.events(); } -void SessionsContent::List::terminating(uint64 hash, bool terminating) { - const auto i = _terminateButtons.find(hash); - if (i != _terminateButtons.cend()) { - if (terminating) { - i->second->clearState(); - i->second->hide(); - } else { - i->second->show(); - } - } +rpl::producer<EntryData> SessionsContent::ListController::showRequests() const { + return _showRequests.events(); } -int SessionsContent::List::resizeGetHeight(int newWidth) { - return _items.size() * st::sessionHeight; -} - -void SessionsContent::List::computeRowWidth() { - const auto available = width() - - st::sessionPadding.left() - - st::sessionTerminateSkip; - _rowWidth = { - .available = available, - .info = available - st::sessionTerminate.width, - }; -} - -void SessionsContent::List::paintEvent(QPaintEvent *e) { - QRect r(e->rect()); - Painter p(this); - - p.fillRect(r, st::boxBg); - p.setFont(st::linkFont); - const auto count = int(_items.size()); - const auto from = floorclamp(r.y(), st::sessionHeight, 0, count); - const auto till = ceilclamp( - r.y() + r.height(), - st::sessionHeight, - 0, - count); - - const auto available = _rowWidth.available; - const auto x = st::sessionPadding.left(); - const auto y = st::sessionPadding.top(); - const auto w = width(); - p.translate(0, from * st::sessionHeight); - for (auto i = from; i != till; ++i) { - const auto &entry = _items[i]; - - p.drawImage(st::sessionUserpicPosition, entry.userpic); - - const auto nameW = _rowWidth.info; - const auto infoW = entry.data.hash ? _rowWidth.info : available; - - p.setPen(st::sessionNameFg); - entry.name.drawLeftElided(p, x, y, nameW, w); - - p.setPen(st::boxTextFg); - entry.info.drawLeftElided(p, x, y + st::sessionInfoTop, infoW, w); - - p.setPen(st::sessionInfoFg); - entry.location.drawLeftElided( - p, - x, - y + st::sessionLocationTop, - available, - w); - - p.translate(0, st::sessionHeight); - } -} - -void SessionsContent::List::mousePressEvent(QMouseEvent *e) { - const auto index = e->pos().y() / st::sessionHeight; - _pressed = (index >= 0 && index < _items.size()) ? index : -1; -} - -void SessionsContent::List::mouseReleaseEvent(QMouseEvent *e) { - const auto index = e->pos().y() / st::sessionHeight; - const auto released = (index >= 0 && index < _items.size()) ? index : -1; - if (released == _pressed && released >= 0) { - _showRequests.fire_copy(_items[released].data); - } - _pressed = -1; +auto SessionsContent::ListController::Add( + not_null<Ui::VerticalLayout*> container, + not_null<Main::Session*> session, + style::margins margins) +-> std::unique_ptr<ListController> { + auto &lifetime = container->lifetime(); + const auto delegate = lifetime.make_state< + PeerListContentDelegateSimple + >(); + auto controller = std::make_unique<ListController>(session); + controller->setStyleOverrides(&st::sessionList); + const auto content = container->add( + object_ptr<PeerListContent>( + container, + controller.get()), + margins); + delegate->setContent(content); + controller->setDelegate(delegate); + return controller; } SessionsBox::SessionsBox( diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index d56433435..c3a5d7122 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -249,23 +249,13 @@ sessionsTerminateAll: SettingsButton(defaultSettingsButton) { } sessionsTerminateAllIcon: icon {{ "settings/devices/terminate_all", attentionButtonFg }}; sessionsTerminateAllIconLeft: 30px; -sessionHeight: 84px; -sessionInfoTop: 21px; -sessionLocationTop: 43px; +sessionLocationTop: 54px; sessionCurrentSkip: 8px; sessionSubtitleSkip: 14px; -sessionPadding: margins(77px, 11px, 22px, 0px); -sessionNameFont: msgNameFont; -sessionNameFg: boxTextFg; -sessionWhenFont: msgDateFont; -sessionWhenFg: windowSubTextFg; -sessionInfoFont: msgFont; +sessionLocationFg: windowSubTextFg; sessionInfoFg: windowSubTextFg; sessionTerminateTop: 9px; sessionTerminateSkip: 22px; -sessionUserpicSize: 42px; -sessionUserpicPosition: point(21px, 10px); -sessionNamePadding: margins(0px, 0px, 5px, 0px); sessionTerminate: IconButton { width: 20px; height: 20px; @@ -280,15 +270,6 @@ sessionTerminate: IconButton { color: windowBgOver; } } -sessionNameStyle: TextStyle(defaultTextStyle) { - font: sessionNameFont; -} -sessionWhenStyle: TextStyle(defaultTextStyle) { - font: sessionWhenFont; -} -sessionInfoStyle: TextStyle(defaultTextStyle) { - font: sessionInfoFont; -} sessionIconWindows: icon{{ "settings/devices/device_desktop_win", historyPeerUserpicFg }}; sessionIconMac: icon{{ "settings/devices/device_desktop_mac", historyPeerUserpicFg }}; sessionIconUbuntu: icon{{ "settings/devices/device_linux_ubuntu", historyPeerUserpicFg }}; @@ -327,3 +308,27 @@ sessionValueLabel: FlatLabel(defaultFlatLabel) { textFg: windowSubTextFg; } sessionValueSkip: 8px; + +sessionListItem: PeerListItem(defaultPeerListItem) { + button: OutlineButton(defaultPeerListButton) { + font: normalFont; + padding: margins(11px, 5px, 11px, 5px); + } + height: 84px; + photoPosition: point(21px, 10px); + nameStyle: TextStyle(defaultTextStyle) { + font: msgNameFont; + } + namePosition: point(77px, 11px); + statusPosition: point(77px, 32px); + photoSize: 42px; + statusFg: boxTextFg; + statusFgOver: boxTextFg; +} +sessionList: PeerList(defaultPeerList) { + item: sessionListItem; + padding: margins(0px, 4px, 0px, 0px); +} +sessionListThreeDotsIcon: icon {{ "info/edit/dotsmini", dialogsMenuIconFg }}; +sessionListThreeDotsIconOver: icon {{ "info/edit/dotsmini", dialogsMenuIconFgOver }}; +sessionListThreeDotsSkip: 12px; From 28eb2f1a91e410529452ad33cf8e13e3f660dc6e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 20:58:53 +0400 Subject: [PATCH 154/180] Return terminate session button to the list. --- Telegram/SourceFiles/boxes/sessions_box.cpp | 21 +++++++++++--------- Telegram/SourceFiles/settings/settings.style | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sessions_box.cpp b/Telegram/SourceFiles/boxes/sessions_box.cpp index 630fcb304..c7c79a257 100644 --- a/Telegram/SourceFiles/boxes/sessions_box.cpp +++ b/Telegram/SourceFiles/boxes/sessions_box.cpp @@ -147,7 +147,6 @@ void RenameBox(not_null<Ui::GenericBox*> box) { } [[nodiscard]] Type TypeFromEntry(const EntryData &entry) { - using List = std::vector<int>; const auto platform = entry.platform.toLower(); const auto device = entry.name.toLower(); const auto system = entry.system.toLower(); @@ -500,6 +499,8 @@ void Row::update(const EntryData &data) { setCustomStatus(_data.info); refreshName(st::sessionListItem); _location.setText(st::defaultTextStyle, LocationAndDate(_data)); + _type = TypeFromEntry(_data); + _userpic = GenerateUserpic(_type); _delegate->rowUpdateRow(this); } @@ -547,15 +548,15 @@ QRect Row::elementGeometry(int element, int outerWidth) const { } break; case 2: { const auto size = QSize( - st::sessionListThreeDotsIcon.width(), - st::sessionListThreeDotsIcon.height()); + st::sessionTerminate.width, + st::sessionTerminate.height); const auto margins = QMargins( 0, (st::sessionListItem.height - size.height()) / 2, st::sessionListThreeDotsSkip, 0); - const auto right = margins.right(); - const auto top = margins.top(); + const auto right = st::sessionTerminateSkip; + const auto top = st::sessionTerminateTop; const auto left = outerWidth - right - size.width(); return QRect(QPoint(left, top), size); } break; @@ -587,10 +588,12 @@ void Row::elementsPaint( int selectedElement) { if (id()) { const auto geometry = elementGeometry(2, outerWidth); + const auto position = geometry.topLeft() + + st::sessionTerminate.iconPosition; const auto &icon = (selectedElement == 2) - ? st::sessionListThreeDotsIconOver - : st::sessionListThreeDotsIcon; - icon.paint(p, geometry.x(), geometry.y(), outerWidth); + ? st::sessionTerminate.iconOver + : st::sessionTerminate.icon; + icon.paint(p, position.x(), position.y(), outerWidth); } p.setFont(st::msgFont); p.setPen(st::sessionInfoFg); @@ -1062,7 +1065,7 @@ void SessionsContent::ListController::rowClicked( void SessionsContent::ListController::rowElementClicked( not_null<PeerListRow*> row, int element) { - if (element == 1) { + if (element == 2) { if (const auto hash = static_cast<Row*>(row.get())->data().hash) { _terminateRequests.fire_copy(hash); } diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index c3a5d7122..6dd224ce4 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -255,7 +255,7 @@ sessionSubtitleSkip: 14px; sessionLocationFg: windowSubTextFg; sessionInfoFg: windowSubTextFg; sessionTerminateTop: 9px; -sessionTerminateSkip: 22px; +sessionTerminateSkip: 12px; sessionTerminate: IconButton { width: 20px; height: 20px; From 60a95df652096d71a2d4ddd12d04497ca3529567 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 30 Nov 2021 22:43:22 +0400 Subject: [PATCH 155/180] Beta version 3.2.7. - Active sessions list redesign. - Fix disappearing emoji selector button. - Fix a crash in archived stickers loading. - Fix a crash in calls to old Telegram versions. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/changelogs.cpp | 10 ++++++++++ Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 7 +++++++ 7 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index f64be4c13..47a42cab7 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="3.2.6.0" /> + Version="3.2.7.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 58780c793..faad508f5 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 3,2,6,0 - PRODUCTVERSION 3,2,6,0 + FILEVERSION 3,2,7,0 + PRODUCTVERSION 3,2,7,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "3.2.6.0" + VALUE "FileVersion", "3.2.7.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.6.0" + VALUE "ProductVersion", "3.2.7.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 471445acb..316d30a29 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 3,2,6,0 - PRODUCTVERSION 3,2,6,0 + FILEVERSION 3,2,7,0 + PRODUCTVERSION 3,2,7,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "3.2.6.0" + VALUE "FileVersion", "3.2.7.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.6.0" + VALUE "ProductVersion", "3.2.7.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/changelogs.cpp b/Telegram/SourceFiles/core/changelogs.cpp index d2295cc42..38e7fadc5 100644 --- a/Telegram/SourceFiles/core/changelogs.cpp +++ b/Telegram/SourceFiles/core/changelogs.cpp @@ -131,6 +131,16 @@ std::map<int, const char*> BetaLogs() { "- Give a custom name to your desktop session " "to distinguish it in the sessions list.\n" + }, + { + 3002007, + "- Active sessions list redesign.\n" + + "- Fix disappearing emoji selector button.\n" + + "- Fix a crash in archived stickers loading.\n" + + "- Fix a crash in calls to old Telegram versions.\n" } }; }; diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 3699ecdcf..26211ce8c 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 3002006; -constexpr auto AppVersionStr = "3.2.6"; +constexpr auto AppVersion = 3002007; +constexpr auto AppVersionStr = "3.2.7"; constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index a58c44520..843c9c28e 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 3002006 +AppVersion 3002007 AppVersionStrMajor 3.2 -AppVersionStrSmall 3.2.6 -AppVersionStr 3.2.6 +AppVersionStrSmall 3.2.7 +AppVersionStr 3.2.7 BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 3.2.6.beta +AppVersionOriginal 3.2.7.beta diff --git a/changelog.txt b/changelog.txt index fd6810eee..b59ae73b6 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +3.2.7 beta (30.11.21) + +- Active sessions list redesign. +- Fix disappearing emoji selector button. +- Fix a crash in archived stickers loading. +- Fix a crash in calls to old Telegram versions. + 3.2.6 beta (26.11.21) - Try out the new audio player with playlist shuffle and repeat. From e5ee665fa45627e40e5400abef5f5adbfc118dfd Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 30 Nov 2021 18:00:32 +0400 Subject: [PATCH 156/180] Add support for webkit2gtk-5.0 and webkit2gtk-4.1 --- Telegram/Resources/langs/lang.strings | 2 +- Telegram/build/docker/centos_env/Dockerfile | 2 +- Telegram/lib_webview | 2 +- cmake | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0ea9630f1..e3da4ba3f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2016,7 +2016,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_payments_webview_no_card" = "Unfortunately, you can't add a new card with current system configuration."; "lng_payments_webview_no_use" = "Unfortunately, you can't use payments with current system configuration."; "lng_payments_webview_install_edge" = "Please install {link}."; -"lng_payments_webview_install_webkit" = "Please install WebKitGTK 4 (webkit2gtk-4.0) using your package manager."; +"lng_payments_webview_install_webkit" = "Please install WebKitGTK (webkit2gtk-5.0/webkit2gtk-4.1/webkit2gtk-4.0) using your package manager."; "lng_payments_webview_switch_mutter" = "Qt's window embedding doesn't work well with Mutter window manager. Please switch to another window manager or desktop environment."; "lng_payments_webview_switch_wayland" = "There is no way to embed WebView window on Wayland. Please switch to X11."; "lng_payments_sure_close" = "Are you sure you want to close this payment form? The changes you made will be lost."; diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 0bc0bf4f8..559a61a5e 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -46,7 +46,7 @@ RUN ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake RUN rm $CMAKE_FILE FROM builder AS patches -RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout 19e5e028c7 +RUN git clone $GIT/desktop-app/patches.git && cd patches && git checkout b6c29e99da FROM builder AS extra-cmake-modules diff --git a/Telegram/lib_webview b/Telegram/lib_webview index 754657aed..8be9c0ff2 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit 754657aededfaa646bc6e8f48a18141982762785 +Subproject commit 8be9c0ff274569cbb1bc9dd4ea9ed9089c37ca93 diff --git a/cmake b/cmake index fdd7abd26..c2b070275 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit fdd7abd2663d4f9f909372aa1dfb914101ce617c +Subproject commit c2b070275eecb546b9899495090d27ce55384917 From 5c91506723235883459a181d42f081260d39cfcf Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Tue, 30 Nov 2021 18:01:06 +0400 Subject: [PATCH 157/180] Update tg_owt in snap --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a65d75ead..e04c866e3 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -474,7 +474,7 @@ parts: webrtc: source: https://github.com/desktop-app/tg_owt.git source-depth: 1 - source-commit: d578c760dc6f1ae5f0f3bb5317b0b2ed04b79138 + source-commit: b02478677baac6d563589f216800ff9cea0fd65c plugin: cmake build-packages: - yasm From 7b4bc6419106e5c891f2320860aa747da2f6fcab Mon Sep 17 00:00:00 2001 From: GitHub Action <action@github.com> Date: Wed, 1 Dec 2021 00:14:22 +0000 Subject: [PATCH 158/180] Update User-Agent for DNS to Chrome 96.0.4664.45. --- .../SourceFiles/mtproto/details/mtproto_domain_resolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp index 57026b5f7..16a24379d 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_domain_resolver.cpp @@ -65,7 +65,7 @@ QByteArray DnsUserAgent() { static const auto kResult = QByteArray( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " - "Chrome/94.0.4606.81 Safari/537.36"); + "Chrome/96.0.4664.45 Safari/537.36"); return kResult; } From f7ec60b958772df9746390656a47b2f643abc634 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 1 Dec 2021 13:28:37 +0400 Subject: [PATCH 159/180] Fix crash in opening shared media with another user. --- Telegram/SourceFiles/info/media/info_media_list_widget.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 5940ca79a..d47c4ea00 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -742,6 +742,9 @@ void ListWidget::start() { } void ListWidget::setupSelectRestriction() { + if (_peer->isUser()) { + return; + } const auto chat = _peer->asChat(); const auto channel = _peer->asChannel(); auto noForwards = chat From 0b308aebb2dc2a81db57708f5ba2fd4fa35e6e8d Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 1 Dec 2021 13:29:53 +0400 Subject: [PATCH 160/180] Beta version 3.2.8. - Fix crash in opening shared media with another user. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 4 ++++ 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 47a42cab7..28ac48d80 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="3.2.7.0" /> + Version="3.2.8.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index faad508f5..452a1d6db 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 3,2,7,0 - PRODUCTVERSION 3,2,7,0 + FILEVERSION 3,2,8,0 + PRODUCTVERSION 3,2,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "3.2.7.0" + VALUE "FileVersion", "3.2.8.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.7.0" + VALUE "ProductVersion", "3.2.8.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 316d30a29..f5127aa9b 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 3,2,7,0 - PRODUCTVERSION 3,2,7,0 + FILEVERSION 3,2,8,0 + PRODUCTVERSION 3,2,8,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "3.2.7.0" + VALUE "FileVersion", "3.2.8.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.7.0" + VALUE "ProductVersion", "3.2.8.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 26211ce8c..5242e78e7 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 3002007; -constexpr auto AppVersionStr = "3.2.7"; +constexpr auto AppVersion = 3002008; +constexpr auto AppVersionStr = "3.2.8"; constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 843c9c28e..844ce9a9c 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 3002007 +AppVersion 3002008 AppVersionStrMajor 3.2 -AppVersionStrSmall 3.2.7 -AppVersionStr 3.2.7 +AppVersionStrSmall 3.2.8 +AppVersionStr 3.2.8 BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 3.2.7.beta +AppVersionOriginal 3.2.8.beta diff --git a/changelog.txt b/changelog.txt index b59ae73b6..e08b73a8e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +3.2.8 beta (01.12.21) + +- Fix crash in opening shared media with another user. + 3.2.7 beta (30.11.21) - Active sessions list redesign. From 8592326a3cbe849e691be6ef108bd495c4319519 Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Wed, 1 Dec 2021 16:54:38 +0400 Subject: [PATCH 161/180] Revert "Use kernel accelerated sendfile to copy files on Linux" This reverts commit 34534a9653fd2308f7b04863f28bb71b741a1890. --- Telegram/SourceFiles/_other/updater_linux.cpp | 33 +++------------ .../platform/linux/specific_linux.cpp | 42 +++---------------- 2 files changed, 10 insertions(+), 65 deletions(-) diff --git a/Telegram/SourceFiles/_other/updater_linux.cpp b/Telegram/SourceFiles/_other/updater_linux.cpp index 233b102f8..40e887c52 100644 --- a/Telegram/SourceFiles/_other/updater_linux.cpp +++ b/Telegram/SourceFiles/_other/updater_linux.cpp @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <cstdio> #include <sys/stat.h> #include <sys/types.h> -#include <sys/sendfile.h> #include <cstdlib> #include <unistd.h> #include <dirent.h> @@ -98,6 +97,11 @@ bool copyFile(const char *from, const char *to, bool writeprotected) { fclose(ffrom); return false; } + static const int BufSize = 65536; + char buf[BufSize]; + while (size_t size = fread(buf, 1, BufSize, ffrom)) { + fwrite(buf, 1, size, fto); + } struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c //let's say this wont fail since you already worked OK on that fp @@ -106,33 +110,6 @@ bool copyFile(const char *from, const char *to, bool writeprotected) { fclose(fto); return false; } - - ssize_t copied = sendfile( - fileno(fto), - fileno(ffrom), - nullptr, - fst.st_size); - - if (copied == -1) { - writeLog( - "Copy by sendfile '%s' to '%s' failed, error: %d, fallback now.", - from, - to, - int(errno)); - static const int BufSize = 65536; - char buf[BufSize]; - while (size_t size = fread(buf, 1, BufSize, ffrom)) { - fwrite(buf, 1, size, fto); - } - } else { - writeLog( - "Copy by sendfile '%s' to '%s' done, size: %d, result: %d.", - from, - to, - int(fst.st_size), - int(copied)); - } - //update to the same uid/gid if (!writeprotected && fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) { fclose(ffrom); diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index b0e90e454..8a4c8c378 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -55,9 +55,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <sys/stat.h> #include <sys/types.h> -#ifdef Q_OS_LINUX -#include <sys/sendfile.h> -#endif // Q_OS_LINUX #include <cstdlib> #include <unistd.h> #include <dirent.h> @@ -878,14 +875,6 @@ void psNewVersion() { void psSendToMenu(bool send, bool silent) { } -void sendfileFallback(FILE *out, FILE *in) { - static const int BufSize = 65536; - char buf[BufSize]; - while (size_t size = fread(buf, 1, BufSize, in)) { - fwrite(buf, 1, size, out); - } -} - bool linuxMoveFile(const char *from, const char *to) { FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb"); if (!ffrom) { @@ -896,6 +885,11 @@ bool linuxMoveFile(const char *from, const char *to) { fclose(ffrom); return false; } + static const int BufSize = 65536; + char buf[BufSize]; + while (size_t size = fread(buf, 1, BufSize, ffrom)) { + fwrite(buf, 1, size, fto); + } struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c //let's say this wont fail since you already worked OK on that fp @@ -904,32 +898,6 @@ bool linuxMoveFile(const char *from, const char *to) { fclose(fto); return false; } - -#ifdef Q_OS_LINUX - ssize_t copied = sendfile( - fileno(fto), - fileno(ffrom), - nullptr, - fst.st_size); - if (copied == -1) { - DEBUG_LOG(("Update Error: " - "Copy by sendfile '%1' to '%2' failed, error: %3, fallback now." - ).arg(from - ).arg(to - ).arg(errno)); - sendfileFallback(fto, ffrom); - } else { - DEBUG_LOG(("Update Info: " - "Copy by sendfile '%1' to '%2' done, size: %3, result: %4." - ).arg(from - ).arg(to - ).arg(fst.st_size - ).arg(copied)); - } -#else // Q_OS_LINUX - sendfileFallback(fto, ffrom); -#endif // Q_OS_LINUX - //update to the same uid/gid if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) { fclose(ffrom); From afcebb136cf49d40c4a0ba275aab95b499d69574 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 1 Dec 2021 18:51:18 +0400 Subject: [PATCH 162/180] Don't use MTP for PeerData::isSelf. --- Telegram/SourceFiles/data/data_peer.cpp | 7 +++++++ Telegram/SourceFiles/data/data_peer.h | 4 +--- Telegram/SourceFiles/data/data_user.cpp | 16 +++++++++++++++- Telegram/SourceFiles/data/data_user.h | 13 ++++--------- Telegram/SourceFiles/main/main_session.cpp | 5 +++-- Telegram/SourceFiles/main/main_session.h | 1 + 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index a18057ca0..5fa677a5b 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -795,6 +795,13 @@ QString PeerData::userName() const { return QString(); } +bool PeerData::isSelf() const { + if (const auto user = asUser()) { + return (user->flags() & UserDataFlag::Self); + } + return false; +} + bool PeerData::isVerified() const { if (const auto user = asUser()) { return user->isVerified(); diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 404c22884..e8f34931c 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -164,9 +164,7 @@ public: [[nodiscard]] bool isChannel() const { return peerIsChannel(id); } - [[nodiscard]] bool isSelf() const { - return (input.type() == mtpc_inputPeerSelf); - } + [[nodiscard]] bool isSelf() const; [[nodiscard]] bool isVerified() const; [[nodiscard]] bool isScam() const; [[nodiscard]] bool isFake() const; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index bf714b4e8..68b79056c 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -25,7 +25,8 @@ using UpdateFlag = Data::PeerUpdate::Flag; } // namespace UserData::UserData(not_null<Data::Session*> owner, PeerId id) -: PeerData(owner, id) { +: PeerData(owner, id) +, _flags((id == owner->session().userPeerId()) ? Flag::Self : Flag(0)) { } bool UserData::canShareThisContact() const { @@ -173,6 +174,19 @@ void UserData::setAccessHash(uint64 accessHash) { } } +void UserData::setFlags(UserDataFlags which) { + _flags.set((flags() & UserDataFlag::Self) + | (which & ~UserDataFlag::Self)); +} + +void UserData::addFlags(UserDataFlags which) { + _flags.add(which & ~UserDataFlag::Self); +} + +void UserData::removeFlags(UserDataFlags which) { + _flags.remove(which & ~UserDataFlag::Self); +} + void UserData::setCallsStatus(CallsStatus callsStatus) { if (callsStatus != _callsStatus) { _callsStatus = callsStatus; diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index be0bbf746..b7e3c6bd9 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -37,6 +37,7 @@ enum class UserDataFlag { Support = (1 << 10), CanPinMessages = (1 << 11), DiscardMinPhoto = (1 << 12), + Self = (1 << 13), }; inline constexpr bool is_flag_type(UserDataFlag) { return true; }; using UserDataFlags = base::flags<UserDataFlag>; @@ -68,21 +69,15 @@ public: } void setAccessHash(uint64 accessHash); - void setFlags(UserDataFlags which) { - _flags.set(which); - } - void addFlags(UserDataFlags which) { - _flags.add(which); - } - void removeFlags(UserDataFlags which) { - _flags.remove(which); - } auto flags() const { return _flags.current(); } auto flagsValue() const { return _flags.value(); } + void setFlags(UserDataFlags which); + void addFlags(UserDataFlags which); + void removeFlags(UserDataFlags which); [[nodiscard]] bool isVerified() const { return flags() & UserDataFlag::Verified; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index b329e30bc..a98e2c5ed 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -78,6 +78,7 @@ Session::Session( , _uploader(std::make_unique<Storage::Uploader>(_api.get())) , _storage(std::make_unique<Storage::Facade>()) , _data(std::make_unique<Data::Session>(this)) +, _userId(user.c_user().vid()) , _user(_data->processUser(user)) , _emojiStickersPack(std::make_unique<Stickers::EmojiPack>(this)) , _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this)) @@ -213,11 +214,11 @@ uint64 Session::uniqueId() const { } UserId Session::userId() const { - return peerToUser(_user->id); + return _userId; } PeerId Session::userPeerId() const { - return _user->id; + return _userId; } bool Session::validateSelf(UserId id) { diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 004a15b4d..b40f908b9 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -179,6 +179,7 @@ private: // _data depends on _downloader / _uploader. const std::unique_ptr<Data::Session> _data; + const UserId _userId; const not_null<UserData*> _user; // _emojiStickersPack depends on _data. From 894e7c5828f47e2d99918ab805f6067c6d6210a1 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Wed, 1 Dec 2021 18:52:16 +0400 Subject: [PATCH 163/180] Fix imported messages without a sender name. --- Telegram/SourceFiles/history/history_item_components.cpp | 2 ++ Telegram/SourceFiles/history/history_message.cpp | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 9f1327872..86aff74bf 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -135,6 +135,8 @@ HiddenSenderInfo::HiddenSenderInfo(const QString &name, bool external) (external ? Ui::EmptyUserpic::ExternalName() : name)) { + Expects(!name.isEmpty()); + nameText.setText(st::msgNameStyle, name, Ui::NameTextOptions()); const auto parts = name.trimmed().split(' ', Qt::SkipEmptyParts); firstName = parts[0]; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index ab8c7f616..12c250fd8 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1176,8 +1176,13 @@ void HistoryMessage::setupForwardedComponent(const CreateConfig &config) { return; } forwarded->originalDate = config.originalDate; - forwarded->originalSender = config.senderOriginal - ? history()->owner().peer(config.senderOriginal).get() + const auto originalSender = config.senderOriginal + ? config.senderOriginal + : !config.senderNameOriginal.isEmpty() + ? PeerId() + : from()->id; + forwarded->originalSender = originalSender + ? history()->owner().peer(originalSender).get() : nullptr; if (!forwarded->originalSender) { forwarded->hiddenSenderInfo = std::make_unique<HiddenSenderInfo>( From c4c234f0d373e3420e0b23500a09fec5452a9052 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 2 Dec 2021 11:51:54 +0400 Subject: [PATCH 164/180] Pass FilterId to pinnedIndexChanged. --- Telegram/SourceFiles/dialogs/dialogs_entry.cpp | 15 ++++++--------- Telegram/SourceFiles/dialogs/dialogs_entry.h | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index 1cf2615f3..a9a707f58 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -63,8 +63,8 @@ Data::Folder *Entry::asFolder() { return _isFolder ? static_cast<Data::Folder*>(this) : nullptr; } -void Entry::pinnedIndexChanged(int was, int now) { - if (session().supportMode()) { +void Entry::pinnedIndexChanged(FilterId filterId, int was, int now) { + if (!filterId && session().supportMode()) { // Force reorder in support mode. _sortKeyInChatList = 0; } @@ -83,15 +83,12 @@ void Entry::cachePinnedIndex(FilterId filterId, int index) { } if (!index) { _pinnedIndex.erase(i); - pinnedIndexChanged(was, index); + } else if (!was) { + _pinnedIndex.emplace(filterId, index); } else { - if (!was) { - _pinnedIndex.emplace(filterId, index); - } else { - i->second = index; - } - pinnedIndexChanged(was, index); + i->second = index; } + pinnedIndexChanged(filterId, was, index); } void Entry::cacheTopPromoted(bool promoted) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index bb4bba3db..bff8ad2a5 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -205,7 +205,7 @@ protected: private: virtual void changedChatListPinHook(); - void pinnedIndexChanged(int was, int now); + void pinnedIndexChanged(FilterId filterId, int was, int now); [[nodiscard]] uint64 computeSortPosition(FilterId filterId) const; void setChatListExistence(bool exists); From caaeff32c5136ab297aa44a7e0b389d6616a826b Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 2 Dec 2021 13:35:38 +0400 Subject: [PATCH 165/180] Move global privacy setting down. --- Telegram/SourceFiles/data/data_session.h | 4 ---- Telegram/SourceFiles/settings/settings_privacy_security.cpp | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index f35c1dbcf..27bd7cb9f 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -782,10 +782,6 @@ private: PhotoData *photo, DocumentData *document); - void folderApplyFields( - not_null<Folder*> folder, - const MTPDfolder &data); - void setPinnedFromDialog(const Dialogs::Key &key, bool pinned); NotifySettings &defaultNotifySettings(not_null<const PeerData*> peer); diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index bf6340dfd..dd1912542 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -945,7 +945,6 @@ void PrivacySecurity::setupContent( }; SetupPrivacy(controller, content, trigger()); - SetupArchiveAndMute(controller, content); SetupSessionsList(controller, content, trigger(), [=](Type type) { _showOther.fire_copy(type); }); @@ -956,6 +955,7 @@ void PrivacySecurity::setupContent( #else // !OS_MAC_STORE && !OS_WIN_STORE AddDivider(content); #endif // !OS_MAC_STORE && !OS_WIN_STORE + SetupArchiveAndMute(controller, content); SetupSelfDestruction(controller, content, trigger()); AddDivider(content); SetupBotsAndWebsites(controller, content); From 0fa458737a2b873b00eab08ee3fe4f4ee2f16b51 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 2 Dec 2021 13:54:30 +0400 Subject: [PATCH 166/180] Fix shared media loading. --- Telegram/SourceFiles/data/data_search_controller.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/data/data_search_controller.cpp b/Telegram/SourceFiles/data/data_search_controller.cpp index cbaad409f..76c2182f0 100644 --- a/Telegram/SourceFiles/data/data_search_controller.cpp +++ b/Telegram/SourceFiles/data/data_search_controller.cpp @@ -85,6 +85,10 @@ std::optional<MTPmessages_Search> PrepareSearchRequest( }(); const auto hash = uint64(0); + const auto mtpOffsetId = int(std::clamp( + offsetId.bare, + int64(0), + int64(0x3FFFFFFF))); return MTPmessages_Search( MTP_flags(0), peer->input, @@ -94,7 +98,7 @@ std::optional<MTPmessages_Search> PrepareSearchRequest( filter, MTP_int(0), // min_date MTP_int(0), // max_date - MTP_int(offsetId), + MTP_int(mtpOffsetId), MTP_int(addOffset), MTP_int(limit), MTP_int(maxId), From db0c57a1862aa8d5a21f681684f1a80e1b5a88d7 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 2 Dec 2021 14:42:33 +0400 Subject: [PATCH 167/180] Fix reply-to-media timestamps in captions. --- Telegram/SourceFiles/history/history_item.h | 4 ++++ .../SourceFiles/history/history_message.cpp | 22 +++++++++++++++--- .../SourceFiles/history/history_message.h | 2 ++ .../view/media/history_view_document.cpp | 9 +------- .../history/view/media/history_view_gif.cpp | 9 +------- .../history/view/media/history_view_media.cpp | 16 +++---------- .../history/view/media/history_view_media.h | 4 +--- .../view/media/history_view_media_grouped.cpp | 23 +------------------ 8 files changed, 32 insertions(+), 57 deletions(-) diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 7ff64716b..82c1c996c 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -343,6 +343,10 @@ public: [[nodiscard]] virtual TextWithEntities originalText() const { return TextWithEntities(); } + [[nodiscard]] virtual auto originalTextWithLocalEntities() const + -> TextWithEntities { + return TextWithEntities(); + } [[nodiscard]] virtual TextForMimeData clipboardText() const { return TextForMimeData(); } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 12c250fd8..aeb8c2ab7 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1536,19 +1536,31 @@ Storage::SharedMediaTypesMask HistoryMessage::sharedMediaTypes() const { } bool HistoryMessage::generateLocalEntitiesByReply() const { + using namespace HistoryView; if (!_media) { return true; + } else if (const auto document = _media->document()) { + return !DurationForTimestampLinks(document); } else if (const auto webpage = _media->webpage()) { - return !webpage->document && webpage->type != WebPageType::Video; + return (webpage->type != WebPageType::Video) + && !DurationForTimestampLinks(webpage); } - return false; + return true; } TextWithEntities HistoryMessage::withLocalEntities( const TextWithEntities &textWithEntities) const { using namespace HistoryView; if (!generateLocalEntitiesByReply()) { - if (const auto webpage = _media ? _media->webpage() : nullptr) { + if (!_media) { + } else if (const auto document = _media->document()) { + if (const auto duration = DurationForTimestampLinks(document)) { + return AddTimestampLinks( + textWithEntities, + duration, + TimestampLinkBase(document, fullId())); + } + } else if (const auto webpage = _media->webpage()) { if (const auto duration = DurationForTimestampLinks(webpage)) { return AddTimestampLinks( textWithEntities, @@ -1712,6 +1724,10 @@ TextWithEntities HistoryMessage::originalText() const { return _text.toTextWithEntities(); } +TextWithEntities HistoryMessage::originalTextWithLocalEntities() const { + return withLocalEntities(originalText()); +} + TextForMimeData HistoryMessage::clipboardText() const { if (emptyText()) { return TextForMimeData(); diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index 1c7888666..7611933e5 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -173,6 +173,8 @@ public: void setText(const TextWithEntities &textWithEntities) override; [[nodiscard]] Ui::Text::IsolatedEmoji isolatedEmoji() const override; [[nodiscard]] TextWithEntities originalText() const override; + [[nodiscard]] auto originalTextWithLocalEntities() const + -> TextWithEntities override; [[nodiscard]] TextForMimeData clipboardText() const override; [[nodiscard]] bool textHasLinks() const override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 93b1bdd8d..7c94afd1e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -1084,14 +1084,7 @@ TextWithEntities Document::getCaption() const { } Ui::Text::String Document::createCaption() { - const auto timestampLinksDuration = DurationForTimestampLinks(_data); - const auto timestampLinkBase = timestampLinksDuration - ? TimestampLinkBase(_data, _realParent->fullId()) - : QString(); - return File::createCaption( - _realParent, - timestampLinksDuration, - timestampLinkBase); + return File::createCaption(_realParent); } bool DrawThumbnailAsSongCover( diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 8d8d7119b..530427fa4 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -1322,14 +1322,7 @@ void Gif::refreshParentId(not_null<HistoryItem*> realParent) { } void Gif::refreshCaption() { - const auto timestampLinksDuration = DurationForTimestampLinks(_data); - const auto timestampLinkBase = timestampLinksDuration - ? TimestampLinkBase(_data, _realParent->fullId()) - : QString(); - _caption = createCaption( - _parent->data(), - timestampLinksDuration, - timestampLinkBase); + _caption = createCaption(_parent->data()); } int Gif::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply, const HistoryMessageForwarded *forwarded) const { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index 5e3cebf77..e97caa050 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -64,7 +64,7 @@ QString TimestampLinkBase( TimeId DurationForTimestampLinks(not_null<WebPageData*> webpage) { if (!webpage->collage.items.empty()) { - return false; + return 0; } else if (const auto document = webpage->document) { return DurationForTimestampLinks(document); } else if (webpage->type != WebPageType::Video @@ -185,12 +185,7 @@ QSize Media::countCurrentSize(int newWidth) { return QSize(qMin(newWidth, maxWidth()), minHeight()); } -Ui::Text::String Media::createCaption( - not_null<HistoryItem*> item, - TimeId timestampLinksDuration, - const QString ×tampLinkBase) const { - Expects(timestampLinksDuration >= 0); - +Ui::Text::String Media::createCaption(not_null<HistoryItem*> item) const { if (item->emptyText()) { return {}; } @@ -203,12 +198,7 @@ Ui::Text::String Media::createCaption( }; result.setMarkedText( st::messageTextStyle, - (timestampLinksDuration - ? AddTimestampLinks( - item->originalText(), - timestampLinksDuration, - timestampLinkBase) - : item->originalText()), + item->originalTextWithLocalEntities(), Ui::ItemTextOptions(item), context); if (const auto width = _parent->skipBlockWidth()) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index 7b39017ea..139e26f0a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -297,9 +297,7 @@ public: protected: [[nodiscard]] QSize countCurrentSize(int newWidth) override; [[nodiscard]] Ui::Text::String createCaption( - not_null<HistoryItem*> item, - TimeId timestampLinksDuration = 0, - const QString ×tampLinkBase = QString()) const; + not_null<HistoryItem*> item) const; virtual void playAnimation(bool autoplay) { } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index aa26f92f2..5d677bef9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -672,28 +672,7 @@ void GroupedMedia::updateNeedBubbleState() { }(); if (captionPart) { const auto &part = (*captionPart); - struct Timestamp { - int duration = 0; - QString base; - }; - const auto timestamp = [&]() -> Timestamp { - const auto document = part->content->getDocument(); - const auto duration = document - ? DurationForTimestampLinks(document) - : TimeId(0); - if (!duration) { - return {}; - } - return { - .duration = duration, - .base = TimestampLinkBase(document, part->item->fullId()), - }; - }(); - _caption = createCaption( - part->item, - timestamp.duration, - timestamp.base); - + _caption = createCaption(part->item); _captionItem = part->item; } else { _captionItem = nullptr; From b9ea5718a2dc6db06cbd98149c60bf5c58d7db02 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 2 Dec 2021 15:00:27 +0400 Subject: [PATCH 168/180] Fix "about request admin panel" box hiding. --- .../SourceFiles/history/view/history_view_contact_status.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp index 4d7932de1..bb385ac3a 100644 --- a/Telegram/SourceFiles/history/view/history_view_contact_status.cpp +++ b/Telegram/SourceFiles/history/view/history_view_contact_status.cpp @@ -564,6 +564,7 @@ void ContactStatus::setupRequestInfoHandler(not_null<PeerData*> peer) { *request = peer->session().api().request( MTPmessages_HidePeerSettingsBar(peer->input) ).send(); + box->closeBox(); }); })); }, _bar.lifetime()); From 235484b719aeca81e13bc8eb780e31f60680a929 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 2 Dec 2021 15:26:58 +0400 Subject: [PATCH 169/180] Fix saving group type without changing username. --- .../boxes/peers/edit_peer_type_box.cpp | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp index 36ccef59a..41140c2e4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_type_box.cpp @@ -78,8 +78,8 @@ public: : tr::lng_manage_peer_channel_type(); } - [[nodiscard]] bool isAllowSave() { - return _isAllowSave; + [[nodiscard]] bool goodUsername() const { + return _goodUsername; } [[nodiscard]] Privacy getPrivacy() const { @@ -144,7 +144,7 @@ private: bool _useLocationPhrases = false; bool _isGroup = false; - bool _isAllowSave = false; + bool _goodUsername = false; base::unique_qptr<Ui::VerticalLayout> _wrap; base::Timer _checkUsernameTimer; @@ -171,7 +171,8 @@ Controller::Controller( , _noForwardsSavedValue(noForwardsSavedValue) , _useLocationPhrases(useLocationPhrases) , _isGroup(_peer->isChat() || _peer->isMegagroup()) -, _isAllowSave(!_usernameSavedValue.value_or(QString()).isEmpty()) +, _goodUsername(!_usernameSavedValue.value_or( + _peer->isChannel() ? _peer->asChannel()->username : QString()).isEmpty()) , _wrap(container) , _checkUsernameTimer([=] { checkUsernameAvailability(); }) { _peer->updateFull(); @@ -236,7 +237,8 @@ void Controller::createContent() { if (_controls.privacy->value() == Privacy::NoUsername) { checkUsernameAvailability(); } - const auto forShowing = _privacySavedValue.value_or(Privacy::NoUsername); + const auto forShowing = _privacySavedValue.value_or( + Privacy::NoUsername); _controls.inviteLinkWrap->toggle( (forShowing != Privacy::HasUsername), anim::type::instant); @@ -333,8 +335,8 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() { Expects(_wrap != nullptr); const auto channel = _peer->asChannel(); - const auto username = - _usernameSavedValue.value_or(channel ? channel->username : QString()); + const auto username = _usernameSavedValue.value_or( + channel ? channel->username : QString()); auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( _wrap, @@ -505,7 +507,7 @@ void Controller::askUsernameRevoke() { } void Controller::usernameChanged() { - _isAllowSave = false; + _goodUsername = false; const auto username = getUsernameInput(); if (username.isEmpty()) { _controls.usernameResult = nullptr; @@ -529,12 +531,12 @@ void Controller::usernameChanged() { } void Controller::showUsernameError(rpl::producer<QString> &&error) { - _isAllowSave = false; + _goodUsername = false; showUsernameResult(std::move(error), &st::editPeerUsernameError); } void Controller::showUsernameGood() { - _isAllowSave = true; + _goodUsername = true; showUsernameResult( tr::lng_create_channel_link_available(), &st::editPeerUsernameGood); @@ -655,7 +657,7 @@ void EditPeerTypeBox::prepare() { if (_savedCallback.has_value()) { addButton(tr::lng_settings_save(), [=] { const auto v = controller->getPrivacy(); - if (!controller->isAllowSave() && (v == Privacy::HasUsername)) { + if ((v == Privacy::HasUsername) && !controller->goodUsername()) { controller->setFocusUsername(); return; } From 1c2ea8d84a05e92cd5a4e16bd82384095ee6a038 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Thu, 2 Dec 2021 16:03:07 +0400 Subject: [PATCH 170/180] Fix semi-transparent inline result thumbnails. --- .../inline_bot_layout_internal.cpp | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 583a9e046..54b4b8ad9 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -322,7 +322,8 @@ void Gif::validateThumbnail( frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), (Images::Option::Smooth - | (good ? Images::Option::None : Images::Option::Blurred)), + | (good ? Images::Option::None : Images::Option::Blurred) + | Images::Option::TransparentBackground), size.width(), size.height()); } @@ -663,7 +664,9 @@ void Photo::validateThumbnail( _thumb = image->pixNoCache( frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), - Images::Option::Smooth | (good ? Images::Option(0) : Images::Option::Blurred), + (Images::Option::Smooth + | (good ? Images::Option(0) : Images::Option::Blurred) + | Images::Option::TransparentBackground), size.width(), size.height()); _thumbGood = good; @@ -819,7 +822,12 @@ void Video::prepareThumbnail(QSize size) const { w = width; } } - _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); + _thumb = thumb->pixNoCache( + w * cIntRetinaFactor(), + h * cIntRetinaFactor(), + Images::Option::Smooth | Images::Option::TransparentBackground, + width, + height); } } @@ -1153,7 +1161,12 @@ void Contact::prepareThumbnail(int width, int height) const { w = width; } } - _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); + _thumb = thumb->pixNoCache( + w * cIntRetinaFactor(), + h * cIntRetinaFactor(), + Images::Option::Smooth | Images::Option::TransparentBackground, + width, + height); } Article::Article( @@ -1306,7 +1319,12 @@ void Article::prepareThumbnail(int width, int height) const { w = width; } } - _thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); + _thumb = thumb->pixNoCache( + w * cIntRetinaFactor(), + h * cIntRetinaFactor(), + Images::Option::Smooth | Images::Option::TransparentBackground, + width, + height); } Game::Game(not_null<Context*> context, not_null<Result*> result) @@ -1524,7 +1542,8 @@ void Game::validateThumbnail(Image *image, QSize size, bool good) const { w * cIntRetinaFactor(), h * cIntRetinaFactor(), (Images::Option::Smooth - | (good ? Images::Option::None : Images::Option::Blurred)), + | (good ? Images::Option::None : Images::Option::Blurred) + | Images::Option::TransparentBackground), size.width(), size.height()); } From 9e5117d336de326a72464d8de987ae6b85026a58 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 3 Dec 2021 14:59:08 +0400 Subject: [PATCH 171/180] Respect autodownload settings in reply previews. --- Telegram/SourceFiles/data/data_document.cpp | 10 +++++-- Telegram/SourceFiles/data/data_document.h | 8 ++++-- .../SourceFiles/data/data_media_types.cpp | 28 ++++++------------- Telegram/SourceFiles/data/data_photo.cpp | 12 ++++++-- Telegram/SourceFiles/data/data_photo.h | 5 +++- .../SourceFiles/data/data_photo_media.cpp | 12 ++++++++ Telegram/SourceFiles/data/data_photo_media.h | 2 ++ .../SourceFiles/data/data_reply_preview.cpp | 10 ++++++- .../SourceFiles/data/data_reply_preview.h | 4 ++- .../SourceFiles/history/history_widget.cpp | 2 +- .../history_view_compose_controls.cpp | 17 ++++++++--- .../view/history_view_webpage_preview.cpp | 14 ++++++---- .../view/history_view_webpage_preview.h | 6 +++- 13 files changed, 91 insertions(+), 39 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index d29c99828..34d3a7304 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -1084,13 +1084,19 @@ bool DocumentData::isStickerSetInstalled() const { } } -Image *DocumentData::getReplyPreview(Data::FileOrigin origin) { +Image *DocumentData::getReplyPreview( + Data::FileOrigin origin, + not_null<PeerData*> context) { if (!hasThumbnail()) { return nullptr; } else if (!_replyPreview) { _replyPreview = std::make_unique<Data::ReplyPreview>(this); } - return _replyPreview->image(origin); + return _replyPreview->image(origin, context); +} + +Image *DocumentData::getReplyPreview(not_null<HistoryItem*> item) { + return getReplyPreview(item->fullId(), item->history()->peer); } bool DocumentData::replyPreviewLoaded() const { diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 1b8c3da61..2275094b2 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -115,7 +115,8 @@ public: void setWaitingForAlbum(); [[nodiscard]] bool waitingForAlbum() const; - [[nodiscard]] const Core::FileLocation &location(bool check = false) const; + [[nodiscard]] const Core::FileLocation &location( + bool check = false) const; void setLocation(const Core::FileLocation &loc); bool saveFromData(); @@ -124,7 +125,10 @@ public: [[nodiscard]] bool saveToCache() const; - [[nodiscard]] Image *getReplyPreview(Data::FileOrigin origin); + [[nodiscard]] Image *getReplyPreview( + Data::FileOrigin origin, + not_null<PeerData*> context); + [[nodiscard]] Image *getReplyPreview(not_null<HistoryItem*> item); [[nodiscard]] bool replyPreviewLoaded() const; [[nodiscard]] StickerData *sticker() const; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index ae4d3c25a..4be34f0f0 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -164,18 +164,8 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; } else if (const auto large = media->image(PhotoSize::Large)) { return { PreparePreviewImage(large, radius), readyCacheKey }; } - const auto allowedToDownload = [&] { - const auto photo = media->owner(); - if (media->loaded() || photo->cancelled()) { - return false; - } - return photo->hasExact(PhotoSize::Small) - || photo->hasExact(PhotoSize::Thumbnail) - || AutoDownload::Should( - photo->session().settings().autoDownload(), - item->history()->peer, - photo); - }(); + const auto allowedToDownload = media->autoLoadThumbnailAllowed( + item->history()->peer); const auto cacheKey = allowedToDownload ? 0 : readyCacheKey; if (allowedToDownload) { media->owner()->load(PhotoSize::Small, item->fullId()); @@ -533,7 +523,7 @@ bool MediaPhoto::hasReplyPreview() const { } Image *MediaPhoto::replyPreview() const { - return _photo->getReplyPreview(parent()->fullId()); + return _photo->getReplyPreview(parent()); } bool MediaPhoto::replyPreviewLoaded() const { @@ -738,7 +728,7 @@ bool MediaFile::hasReplyPreview() const { } Image *MediaFile::replyPreview() const { - return _document->getReplyPreview(parent()->fullId()); + return _document->getReplyPreview(parent()); } bool MediaFile::replyPreviewLoaded() const { @@ -1295,9 +1285,9 @@ bool MediaWebPage::hasReplyPreview() const { Image *MediaWebPage::replyPreview() const { if (const auto document = MediaWebPage::document()) { - return document->getReplyPreview(parent()->fullId()); + return document->getReplyPreview(parent()); } else if (const auto photo = MediaWebPage::photo()) { - return photo->getReplyPreview(parent()->fullId()); + return photo->getReplyPreview(parent()); } return nullptr; } @@ -1368,9 +1358,9 @@ bool MediaGame::hasReplyPreview() const { Image *MediaGame::replyPreview() const { if (const auto document = _game->document) { - return document->getReplyPreview(parent()->fullId()); + return document->getReplyPreview(parent()); } else if (const auto photo = _game->photo) { - return photo->getReplyPreview(parent()->fullId()); + return photo->getReplyPreview(parent()); } return nullptr; } @@ -1478,7 +1468,7 @@ bool MediaInvoice::hasReplyPreview() const { Image *MediaInvoice::replyPreview() const { if (const auto photo = _invoice.photo) { - return photo->getReplyPreview(parent()->fullId()); + return photo->getReplyPreview(parent()); } return nullptr; } diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 08676cad0..153456d52 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo_media.h" #include "ui/image/image.h" #include "main/main_session.h" +#include "history/history.h" +#include "history/history_item.h" #include "media/streaming/media_streaming_loader_local.h" #include "media/streaming/media_streaming_loader_mtproto.h" #include "mainwidget.h" @@ -206,11 +208,17 @@ bool PhotoData::uploading() const { return (uploadingData != nullptr); } -Image *PhotoData::getReplyPreview(Data::FileOrigin origin) { +Image *PhotoData::getReplyPreview( + Data::FileOrigin origin, + not_null<PeerData*> context) { if (!_replyPreview) { _replyPreview = std::make_unique<Data::ReplyPreview>(this); } - return _replyPreview->image(origin); + return _replyPreview->image(origin, context); +} + +Image *PhotoData::getReplyPreview(not_null<HistoryItem*> item) { + return getReplyPreview(item->fullId(), item->history()->peer); } bool PhotoData::replyPreviewLoaded() const { diff --git a/Telegram/SourceFiles/data/data_photo.h b/Telegram/SourceFiles/data/data_photo.h index 4922b28d3..4e6127965 100644 --- a/Telegram/SourceFiles/data/data_photo.h +++ b/Telegram/SourceFiles/data/data_photo.h @@ -64,7 +64,10 @@ public: void setWaitingForAlbum(); [[nodiscard]] bool waitingForAlbum() const; - [[nodiscard]] Image *getReplyPreview(Data::FileOrigin origin); + [[nodiscard]] Image *getReplyPreview( + Data::FileOrigin origin, + not_null<PeerData*> context); + [[nodiscard]] Image *getReplyPreview(not_null<HistoryItem*> item); [[nodiscard]] bool replyPreviewLoaded() const; void setRemoteLocation( diff --git a/Telegram/SourceFiles/data/data_photo_media.cpp b/Telegram/SourceFiles/data/data_photo_media.cpp index 19035b346..620d7429e 100644 --- a/Telegram/SourceFiles/data/data_photo_media.cpp +++ b/Telegram/SourceFiles/data/data_photo_media.cpp @@ -128,6 +128,18 @@ float64 PhotoMedia::progress() const { : (loaded() ? 1. : 0.); } +bool PhotoMedia::autoLoadThumbnailAllowed(not_null<PeerData*> peer) const { + if (loaded() || _owner->cancelled()) { + return false; + } + return _owner->hasExact(PhotoSize::Small) + || _owner->hasExact(PhotoSize::Thumbnail) + || AutoDownload::Should( + _owner->session().settings().autoDownload(), + peer, + _owner); +} + void PhotoMedia::automaticLoad( Data::FileOrigin origin, const HistoryItem *item) { diff --git a/Telegram/SourceFiles/data/data_photo_media.h b/Telegram/SourceFiles/data/data_photo_media.h index 42f52b805..7a8b095d9 100644 --- a/Telegram/SourceFiles/data/data_photo_media.h +++ b/Telegram/SourceFiles/data/data_photo_media.h @@ -36,6 +36,8 @@ public: [[nodiscard]] bool loaded() const; [[nodiscard]] float64 progress() const; + [[nodiscard]] bool autoLoadThumbnailAllowed( + not_null<PeerData*> peer) const; void automaticLoad(Data::FileOrigin origin, const HistoryItem *item); void collectLocalData(not_null<PhotoMedia*> local); diff --git a/Telegram/SourceFiles/data/data_reply_preview.cpp b/Telegram/SourceFiles/data/data_reply_preview.cpp index 1067e2346..39da244df 100644 --- a/Telegram/SourceFiles/data/data_reply_preview.cpp +++ b/Telegram/SourceFiles/data/data_reply_preview.cpp @@ -55,7 +55,9 @@ void ReplyPreview::prepare(not_null<Image*> image, Images::Options options) { _good = ((options & Images::Option::Blurred) == 0); } -Image *ReplyPreview::image(Data::FileOrigin origin) { +Image *ReplyPreview::image( + Data::FileOrigin origin, + not_null<PeerData*> context) { if (_checked) { return _image.get(); } @@ -84,8 +86,14 @@ Image *ReplyPreview::image(Data::FileOrigin origin) { } else { Assert(_photo != nullptr); if (!_image || !_good) { + const auto createMedia = !_photoMedia; + const auto inlineThumbnailBytes = _photo->inlineThumbnailBytes(); if (!_photoMedia) { _photoMedia = _photo->createMediaView(); + } + const auto loadThumbnail = inlineThumbnailBytes.isEmpty() + || _photoMedia->autoLoadThumbnailAllowed(context); + if (loadThumbnail) { _photoMedia->wanted(PhotoSize::Small, origin); } if (const auto small = _photoMedia->image(PhotoSize::Small)) { diff --git a/Telegram/SourceFiles/data/data_reply_preview.h b/Telegram/SourceFiles/data/data_reply_preview.h index 01314e7ef..04abeb433 100644 --- a/Telegram/SourceFiles/data/data_reply_preview.h +++ b/Telegram/SourceFiles/data/data_reply_preview.h @@ -23,7 +23,9 @@ public: explicit ReplyPreview(not_null<PhotoData*> photo); ~ReplyPreview(); - [[nodiscard]] Image *image(Data::FileOrigin origin); + [[nodiscard]] Image *image( + Data::FileOrigin origin, + not_null<PeerData*> context); [[nodiscard]] bool loaded() const; private: diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9eec7f22a..863f8da9e 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -7162,7 +7162,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { textTop, st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); - if (HistoryView::DrawWebPageDataPreview(p, _previewData, to)) { + if (HistoryView::DrawWebPageDataPreview(p, _previewData, _peer, to)) { previewLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 599a7cfe8..fa34f92a8 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -105,6 +105,7 @@ class FieldHeader final : public Ui::RpWidget { public: FieldHeader(QWidget *parent, not_null<Data::Session*> data); + void setHistory(const SetHistoryArgs &args); void init(); void editMessage(FullMsgId id); @@ -142,7 +143,7 @@ private: void resolveMessageData(); void updateShownMessageText(); - void paintWebPage(Painter &p); + void paintWebPage(Painter &p, not_null<PeerData*> peer); void paintEditOrReplyToMessage(Painter &p); struct Preview { @@ -152,6 +153,7 @@ private: bool cancelled = false; }; + History *_history = nullptr; rpl::variable<QString> _title; rpl::variable<QString> _description; @@ -188,6 +190,10 @@ FieldHeader::FieldHeader(QWidget *parent, not_null<Data::Session*> data) init(); } +void FieldHeader::setHistory(const SetHistoryArgs &args) { + _history = *args.history; +} + void FieldHeader::init() { sizeValue( ) | rpl::start_with_next([=](QSize size) { @@ -209,7 +215,9 @@ void FieldHeader::init() { (!ShowWebPagePreview(_preview.data) || *leftIconPressed) ? paintEditOrReplyToMessage(p) - : paintWebPage(p); + : paintWebPage( + p, + _history ? _history->peer : _data->session().user()); }, lifetime()); _editMsgId.value( @@ -415,7 +423,7 @@ void FieldHeader::previewRequested( } -void FieldHeader::paintWebPage(Painter &p) { +void FieldHeader::paintWebPage(Painter &p, not_null<PeerData*> context) { Expects(ShowWebPagePreview(_preview.data)); const auto textTop = st::msgReplyPadding.top(); @@ -432,7 +440,7 @@ void FieldHeader::paintWebPage(Painter &p) { textTop, st::msgReplyBarSize.height(), st::msgReplyBarSize.height()); - if (HistoryView::DrawWebPageDataPreview(p, _preview.data, to)) { + if (HistoryView::DrawWebPageDataPreview(p, _preview.data, context, to)) { previewLeft += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() @@ -655,6 +663,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) { //} unregisterDraftSources(); _history = history; + _header->setHistory(args); registerDraftSource(); _window->tabbedSelector()->setCurrentPeer( history ? history->peer.get() : nullptr); diff --git a/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp b/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp index d7d5398c9..01b83838e 100644 --- a/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp +++ b/Telegram/SourceFiles/history/view/history_view_webpage_preview.cpp @@ -56,9 +56,13 @@ WebPageText TitleAndDescriptionFromWebPage(not_null<WebPageData*> d) { return { resultTitle, resultDescription }; } -bool DrawWebPageDataPreview(Painter &p, not_null<WebPageData*> d, QRect to) { - const auto document = d->document; - const auto photo = d->photo; +bool DrawWebPageDataPreview( + Painter &p, + not_null<WebPageData*> webpage, + not_null<PeerData*> context, + QRect to) { + const auto document = webpage->document; + const auto photo = webpage->photo; if ((!photo || photo->isNull()) && (!document || !document->hasThumbnail() @@ -67,8 +71,8 @@ bool DrawWebPageDataPreview(Painter &p, not_null<WebPageData*> d, QRect to) { } const auto preview = photo - ? photo->getReplyPreview(Data::FileOrigin()) - : document->getReplyPreview(Data::FileOrigin()); + ? photo->getReplyPreview(Data::FileOrigin(), context) + : document->getReplyPreview(Data::FileOrigin(), context); if (preview) { const auto w = preview->width(); const auto h = preview->height(); diff --git a/Telegram/SourceFiles/history/view/history_view_webpage_preview.h b/Telegram/SourceFiles/history/view/history_view_webpage_preview.h index 54594c28a..e20ad9232 100644 --- a/Telegram/SourceFiles/history/view/history_view_webpage_preview.h +++ b/Telegram/SourceFiles/history/view/history_view_webpage_preview.h @@ -15,6 +15,10 @@ struct WebPageText { }; WebPageText TitleAndDescriptionFromWebPage(not_null<WebPageData*> d); -bool DrawWebPageDataPreview(Painter &p, not_null<WebPageData*> d, QRect to); +bool DrawWebPageDataPreview( + Painter &p, + not_null<WebPageData*> webpage, + not_null<PeerData*> context, + QRect to); } // namespace HistoryView From ebd958782135eddff3f4013ee4837db8187e45d3 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 3 Dec 2021 15:02:45 +0400 Subject: [PATCH 172/180] Fix admin ranks in participants edit. --- Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp index cd0c9df29..8fd36a7cc 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_participants_box.cpp @@ -662,7 +662,7 @@ UserData *ParticipantsAdditionalData::applyCreator( } else { _adminCanEdit.erase(user); } - if (data.rank().isEmpty()) { + if (!data.rank().isEmpty()) { _adminRanks[user] = data.rank(); } else { _adminRanks.remove(user); @@ -693,7 +693,7 @@ UserData *ParticipantsAdditionalData::applyAdmin( } else { _adminCanEdit.erase(user); } - if (data.rank().isEmpty()) { + if (!data.rank().isEmpty()) { _adminRanks[user] = data.rank(); } else { _adminRanks.remove(user); From e708b2d39cb078d3942d8a76de816037e9bd5f2a Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Fri, 3 Dec 2021 15:29:33 +0400 Subject: [PATCH 173/180] Move some icons, fix verified check scaling. --- .../icons/{ => dialogs}/dialogs_bot.png | Bin .../icons/{ => dialogs}/dialogs_bot@2x.png | Bin .../icons/{ => dialogs}/dialogs_bot@3x.png | Bin .../icons/{ => dialogs}/dialogs_calendar.png | Bin .../{ => dialogs}/dialogs_calendar@2x.png | Bin .../{ => dialogs}/dialogs_calendar@3x.png | Bin .../{ => dialogs}/dialogs_cancel_search.png | Bin .../dialogs_cancel_search@2x.png | Bin .../dialogs_cancel_search@3x.png | Bin .../icons/{ => dialogs}/dialogs_channel.png | Bin .../{ => dialogs}/dialogs_channel@2x.png | Bin .../{ => dialogs}/dialogs_channel@3x.png | Bin .../icons/{ => dialogs}/dialogs_chat.png | Bin .../icons/{ => dialogs}/dialogs_chat@2x.png | Bin .../icons/{ => dialogs}/dialogs_chat@3x.png | Bin .../icons/{ => dialogs}/dialogs_lock.png | Bin .../icons/{ => dialogs}/dialogs_lock@2x.png | Bin .../icons/{ => dialogs}/dialogs_lock@3x.png | Bin .../icons/{ => dialogs}/dialogs_menu.png | Bin .../icons/{ => dialogs}/dialogs_menu@2x.png | Bin .../icons/{ => dialogs}/dialogs_menu@3x.png | Bin .../{ => dialogs}/dialogs_menu_unread.png | Bin .../{ => dialogs}/dialogs_menu_unread@2x.png | Bin .../{ => dialogs}/dialogs_menu_unread@3x.png | Bin .../{ => dialogs}/dialogs_menu_unread_dot.png | Bin .../dialogs_menu_unread_dot@2x.png | Bin .../dialogs_menu_unread_dot@3x.png | Bin .../icons/{ => dialogs}/dialogs_pinned.png | Bin .../icons/{ => dialogs}/dialogs_pinned@2x.png | Bin .../icons/{ => dialogs}/dialogs_pinned@3x.png | Bin .../icons/{ => dialogs}/dialogs_received.png | Bin .../{ => dialogs}/dialogs_received@2x.png | Bin .../{ => dialogs}/dialogs_received@3x.png | Bin .../{ => dialogs}/dialogs_search_from.png | Bin .../{ => dialogs}/dialogs_search_from@2x.png | Bin .../{ => dialogs}/dialogs_search_from@3x.png | Bin .../icons/{ => dialogs}/dialogs_sending.png | Bin .../{ => dialogs}/dialogs_sending@2x.png | Bin .../{ => dialogs}/dialogs_sending@3x.png | Bin .../icons/{ => dialogs}/dialogs_sent.png | Bin .../icons/{ => dialogs}/dialogs_sent@2x.png | Bin .../icons/{ => dialogs}/dialogs_sent@3x.png | Bin .../icons/{ => dialogs}/dialogs_unlock.png | Bin .../icons/{ => dialogs}/dialogs_unlock@2x.png | Bin .../icons/{ => dialogs}/dialogs_unlock@3x.png | Bin .../icons/dialogs/dialogs_verified_check.png | Bin 0 -> 233 bytes .../dialogs/dialogs_verified_check@2x.png | Bin 0 -> 365 bytes .../dialogs/dialogs_verified_check@3x.png | Bin 0 -> 425 bytes .../icons/dialogs/dialogs_verified_star.png | Bin 0 -> 371 bytes .../dialogs/dialogs_verified_star@2x.png | Bin 0 -> 714 bytes .../dialogs/dialogs_verified_star@3x.png | Bin 0 -> 803 bytes .../icons/dialogs_verified_check.png | Bin 180 -> 0 bytes .../icons/dialogs_verified_check@2x.png | Bin 281 -> 0 bytes .../icons/dialogs_verified_check@3x.png | Bin 404 -> 0 bytes .../Resources/icons/dialogs_verified_star.png | Bin 342 -> 0 bytes .../icons/dialogs_verified_star@2x.png | Bin 660 -> 0 bytes .../icons/dialogs_verified_star@3x.png | Bin 694 -> 0 bytes Telegram/SourceFiles/dialogs/dialogs.style | 94 +++++++++--------- Telegram/SourceFiles/window/window.style | 10 +- 59 files changed, 52 insertions(+), 52 deletions(-) rename Telegram/Resources/icons/{ => dialogs}/dialogs_bot.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_bot@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_bot@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_calendar.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_calendar@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_calendar@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_cancel_search.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_cancel_search@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_cancel_search@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_channel.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_channel@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_channel@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_chat.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_chat@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_chat@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_lock.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_lock@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_lock@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu_unread.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu_unread@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu_unread@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu_unread_dot.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu_unread_dot@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_menu_unread_dot@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_pinned.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_pinned@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_pinned@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_received.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_received@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_received@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_search_from.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_search_from@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_search_from@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_sending.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_sending@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_sending@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_sent.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_sent@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_sent@3x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_unlock.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_unlock@2x.png (100%) rename Telegram/Resources/icons/{ => dialogs}/dialogs_unlock@3x.png (100%) create mode 100644 Telegram/Resources/icons/dialogs/dialogs_verified_check.png create mode 100644 Telegram/Resources/icons/dialogs/dialogs_verified_check@2x.png create mode 100644 Telegram/Resources/icons/dialogs/dialogs_verified_check@3x.png create mode 100644 Telegram/Resources/icons/dialogs/dialogs_verified_star.png create mode 100644 Telegram/Resources/icons/dialogs/dialogs_verified_star@2x.png create mode 100644 Telegram/Resources/icons/dialogs/dialogs_verified_star@3x.png delete mode 100644 Telegram/Resources/icons/dialogs_verified_check.png delete mode 100644 Telegram/Resources/icons/dialogs_verified_check@2x.png delete mode 100644 Telegram/Resources/icons/dialogs_verified_check@3x.png delete mode 100644 Telegram/Resources/icons/dialogs_verified_star.png delete mode 100644 Telegram/Resources/icons/dialogs_verified_star@2x.png delete mode 100644 Telegram/Resources/icons/dialogs_verified_star@3x.png diff --git a/Telegram/Resources/icons/dialogs_bot.png b/Telegram/Resources/icons/dialogs/dialogs_bot.png similarity index 100% rename from Telegram/Resources/icons/dialogs_bot.png rename to Telegram/Resources/icons/dialogs/dialogs_bot.png diff --git a/Telegram/Resources/icons/dialogs_bot@2x.png b/Telegram/Resources/icons/dialogs/dialogs_bot@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_bot@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_bot@2x.png diff --git a/Telegram/Resources/icons/dialogs_bot@3x.png b/Telegram/Resources/icons/dialogs/dialogs_bot@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_bot@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_bot@3x.png diff --git a/Telegram/Resources/icons/dialogs_calendar.png b/Telegram/Resources/icons/dialogs/dialogs_calendar.png similarity index 100% rename from Telegram/Resources/icons/dialogs_calendar.png rename to Telegram/Resources/icons/dialogs/dialogs_calendar.png diff --git a/Telegram/Resources/icons/dialogs_calendar@2x.png b/Telegram/Resources/icons/dialogs/dialogs_calendar@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_calendar@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_calendar@2x.png diff --git a/Telegram/Resources/icons/dialogs_calendar@3x.png b/Telegram/Resources/icons/dialogs/dialogs_calendar@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_calendar@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_calendar@3x.png diff --git a/Telegram/Resources/icons/dialogs_cancel_search.png b/Telegram/Resources/icons/dialogs/dialogs_cancel_search.png similarity index 100% rename from Telegram/Resources/icons/dialogs_cancel_search.png rename to Telegram/Resources/icons/dialogs/dialogs_cancel_search.png diff --git a/Telegram/Resources/icons/dialogs_cancel_search@2x.png b/Telegram/Resources/icons/dialogs/dialogs_cancel_search@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_cancel_search@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_cancel_search@2x.png diff --git a/Telegram/Resources/icons/dialogs_cancel_search@3x.png b/Telegram/Resources/icons/dialogs/dialogs_cancel_search@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_cancel_search@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_cancel_search@3x.png diff --git a/Telegram/Resources/icons/dialogs_channel.png b/Telegram/Resources/icons/dialogs/dialogs_channel.png similarity index 100% rename from Telegram/Resources/icons/dialogs_channel.png rename to Telegram/Resources/icons/dialogs/dialogs_channel.png diff --git a/Telegram/Resources/icons/dialogs_channel@2x.png b/Telegram/Resources/icons/dialogs/dialogs_channel@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_channel@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_channel@2x.png diff --git a/Telegram/Resources/icons/dialogs_channel@3x.png b/Telegram/Resources/icons/dialogs/dialogs_channel@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_channel@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_channel@3x.png diff --git a/Telegram/Resources/icons/dialogs_chat.png b/Telegram/Resources/icons/dialogs/dialogs_chat.png similarity index 100% rename from Telegram/Resources/icons/dialogs_chat.png rename to Telegram/Resources/icons/dialogs/dialogs_chat.png diff --git a/Telegram/Resources/icons/dialogs_chat@2x.png b/Telegram/Resources/icons/dialogs/dialogs_chat@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_chat@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_chat@2x.png diff --git a/Telegram/Resources/icons/dialogs_chat@3x.png b/Telegram/Resources/icons/dialogs/dialogs_chat@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_chat@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_chat@3x.png diff --git a/Telegram/Resources/icons/dialogs_lock.png b/Telegram/Resources/icons/dialogs/dialogs_lock.png similarity index 100% rename from Telegram/Resources/icons/dialogs_lock.png rename to Telegram/Resources/icons/dialogs/dialogs_lock.png diff --git a/Telegram/Resources/icons/dialogs_lock@2x.png b/Telegram/Resources/icons/dialogs/dialogs_lock@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_lock@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_lock@2x.png diff --git a/Telegram/Resources/icons/dialogs_lock@3x.png b/Telegram/Resources/icons/dialogs/dialogs_lock@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_lock@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_lock@3x.png diff --git a/Telegram/Resources/icons/dialogs_menu.png b/Telegram/Resources/icons/dialogs/dialogs_menu.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu.png rename to Telegram/Resources/icons/dialogs/dialogs_menu.png diff --git a/Telegram/Resources/icons/dialogs_menu@2x.png b/Telegram/Resources/icons/dialogs/dialogs_menu@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_menu@2x.png diff --git a/Telegram/Resources/icons/dialogs_menu@3x.png b/Telegram/Resources/icons/dialogs/dialogs_menu@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_menu@3x.png diff --git a/Telegram/Resources/icons/dialogs_menu_unread.png b/Telegram/Resources/icons/dialogs/dialogs_menu_unread.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu_unread.png rename to Telegram/Resources/icons/dialogs/dialogs_menu_unread.png diff --git a/Telegram/Resources/icons/dialogs_menu_unread@2x.png b/Telegram/Resources/icons/dialogs/dialogs_menu_unread@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu_unread@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_menu_unread@2x.png diff --git a/Telegram/Resources/icons/dialogs_menu_unread@3x.png b/Telegram/Resources/icons/dialogs/dialogs_menu_unread@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu_unread@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_menu_unread@3x.png diff --git a/Telegram/Resources/icons/dialogs_menu_unread_dot.png b/Telegram/Resources/icons/dialogs/dialogs_menu_unread_dot.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu_unread_dot.png rename to Telegram/Resources/icons/dialogs/dialogs_menu_unread_dot.png diff --git a/Telegram/Resources/icons/dialogs_menu_unread_dot@2x.png b/Telegram/Resources/icons/dialogs/dialogs_menu_unread_dot@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu_unread_dot@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_menu_unread_dot@2x.png diff --git a/Telegram/Resources/icons/dialogs_menu_unread_dot@3x.png b/Telegram/Resources/icons/dialogs/dialogs_menu_unread_dot@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_menu_unread_dot@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_menu_unread_dot@3x.png diff --git a/Telegram/Resources/icons/dialogs_pinned.png b/Telegram/Resources/icons/dialogs/dialogs_pinned.png similarity index 100% rename from Telegram/Resources/icons/dialogs_pinned.png rename to Telegram/Resources/icons/dialogs/dialogs_pinned.png diff --git a/Telegram/Resources/icons/dialogs_pinned@2x.png b/Telegram/Resources/icons/dialogs/dialogs_pinned@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_pinned@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_pinned@2x.png diff --git a/Telegram/Resources/icons/dialogs_pinned@3x.png b/Telegram/Resources/icons/dialogs/dialogs_pinned@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_pinned@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_pinned@3x.png diff --git a/Telegram/Resources/icons/dialogs_received.png b/Telegram/Resources/icons/dialogs/dialogs_received.png similarity index 100% rename from Telegram/Resources/icons/dialogs_received.png rename to Telegram/Resources/icons/dialogs/dialogs_received.png diff --git a/Telegram/Resources/icons/dialogs_received@2x.png b/Telegram/Resources/icons/dialogs/dialogs_received@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_received@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_received@2x.png diff --git a/Telegram/Resources/icons/dialogs_received@3x.png b/Telegram/Resources/icons/dialogs/dialogs_received@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_received@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_received@3x.png diff --git a/Telegram/Resources/icons/dialogs_search_from.png b/Telegram/Resources/icons/dialogs/dialogs_search_from.png similarity index 100% rename from Telegram/Resources/icons/dialogs_search_from.png rename to Telegram/Resources/icons/dialogs/dialogs_search_from.png diff --git a/Telegram/Resources/icons/dialogs_search_from@2x.png b/Telegram/Resources/icons/dialogs/dialogs_search_from@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_search_from@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_search_from@2x.png diff --git a/Telegram/Resources/icons/dialogs_search_from@3x.png b/Telegram/Resources/icons/dialogs/dialogs_search_from@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_search_from@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_search_from@3x.png diff --git a/Telegram/Resources/icons/dialogs_sending.png b/Telegram/Resources/icons/dialogs/dialogs_sending.png similarity index 100% rename from Telegram/Resources/icons/dialogs_sending.png rename to Telegram/Resources/icons/dialogs/dialogs_sending.png diff --git a/Telegram/Resources/icons/dialogs_sending@2x.png b/Telegram/Resources/icons/dialogs/dialogs_sending@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_sending@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_sending@2x.png diff --git a/Telegram/Resources/icons/dialogs_sending@3x.png b/Telegram/Resources/icons/dialogs/dialogs_sending@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_sending@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_sending@3x.png diff --git a/Telegram/Resources/icons/dialogs_sent.png b/Telegram/Resources/icons/dialogs/dialogs_sent.png similarity index 100% rename from Telegram/Resources/icons/dialogs_sent.png rename to Telegram/Resources/icons/dialogs/dialogs_sent.png diff --git a/Telegram/Resources/icons/dialogs_sent@2x.png b/Telegram/Resources/icons/dialogs/dialogs_sent@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_sent@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_sent@2x.png diff --git a/Telegram/Resources/icons/dialogs_sent@3x.png b/Telegram/Resources/icons/dialogs/dialogs_sent@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_sent@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_sent@3x.png diff --git a/Telegram/Resources/icons/dialogs_unlock.png b/Telegram/Resources/icons/dialogs/dialogs_unlock.png similarity index 100% rename from Telegram/Resources/icons/dialogs_unlock.png rename to Telegram/Resources/icons/dialogs/dialogs_unlock.png diff --git a/Telegram/Resources/icons/dialogs_unlock@2x.png b/Telegram/Resources/icons/dialogs/dialogs_unlock@2x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_unlock@2x.png rename to Telegram/Resources/icons/dialogs/dialogs_unlock@2x.png diff --git a/Telegram/Resources/icons/dialogs_unlock@3x.png b/Telegram/Resources/icons/dialogs/dialogs_unlock@3x.png similarity index 100% rename from Telegram/Resources/icons/dialogs_unlock@3x.png rename to Telegram/Resources/icons/dialogs/dialogs_unlock@3x.png diff --git a/Telegram/Resources/icons/dialogs/dialogs_verified_check.png b/Telegram/Resources/icons/dialogs/dialogs_verified_check.png new file mode 100644 index 0000000000000000000000000000000000000000..14dfa4ee68d4c0af66bda82dc5763e9f55d65b18 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9Q!3HFy+4N(86lZ})WHAE+w=f7ZGR&GI0Ti6= z>Eak-(ffANM$W?q0<Py78@;Y(-Qm`KAjvY%(K~}J^p;Bnr)iYa1J*k`MB3(`(wr0{ zDz5nCpy+YixKd`pM!g4Ru21BDeVY)}Rdnvj-uj)5(weGkw)WS~QrmK^K!!cY>%5(O z)HK%L`wm;S3ouo^zp;(?v(5g`KPMQnz25a`$&{u-8O!<nf`VJFt=Ot`j6;#-HE;Uc e;JOF>7Vl=wEl%WmzxNE#nGBw;elF{r5}E)u4^>P6 literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs/dialogs_verified_check@2x.png b/Telegram/Resources/icons/dialogs/dialogs_verified_check@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1caf9a6853fb27165dd079c2fc2d8432411e3c27 GIT binary patch literal 365 zcmV-z0h0cSP)<h;3K|Lk000e1NJLTq001Na001Be1^@s6dXd_W00009a7bBm000XU z000XU0RWnu7ytkPA4x<(R9J=W)v=0$FdWD6^h9tBf^>IwcXJYVmp((5zCgjzXXxnY z3%H2j=pwjec6aL}LJ9mjRcenTYVl6Ed?8EFzn{U710e)tM}7S@;5`MBr9iS2NR|T0 zQXp9hB<oi|nx>IBj!6_nv!2J6%P<V2X$k-kLLdx7**n`|j>jXas={komTe32eIMuZ z`P;tZIB45;f=QADx~^{r@;ncH-$Mv7K?Om8+wC?&gb=t~E_3Iu2Ngvz`6y!yj4|Br z_i=w12Aoc(C3Dw<D5a?DdV+eb>pHluyL9en|Bz{#XqpD^an51e_L`ZWfe0b6EDN0T z5tHXRD5Y!XeGYn}%Cf}udYyBUeq4vAYA^lIyKG4cBujy0?GE$+(offZb%oa=00000 LNkvXXu0mjfxS^Sz literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs/dialogs_verified_check@3x.png b/Telegram/Resources/icons/dialogs/dialogs_verified_check@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b4cea40f7029179f98849b1fdd37424a821363 GIT binary patch literal 425 zcmV;a0apHrP)<h;3K|Lk000e1NJLTq001@s001xu0ssI2)qWnA00009a7bBm000XU z000XU0RWnu7ytkPTS-JgR9J=W*0HOCKpepFJ9E+`{09PY@NZ~run-)KXmDuPq5q=A zB?u%O`UetP96}tWJ!oj?F9;$>DTQp17niTz_PqDt_Zg3a-<P`^4j}*l000000094) zX_~9ms?liB4tWP-Je^MO_j^$ki^YO=&Nn>Iuh;8>VB0qBC_=m4-tBh9tNIR-=Xs3r z4;wn2&gpa_<MKS8OeQt;HJi=tcDr0IKUI#$<LmWG_9aQu>-B2t<2Wvk<8rX)d6i0r zVI0S)T<-CBR1~GAK7`O_vmwX3uKTPM1R)55N?#O3qA1qahY(U#HBD18Vm_Z^j3r6h z@As8H*L7Kzt+lVz@AoTD&G-HD`An{4S!P)lg}5h8({8t0@xkSC>2|v``%1d5zpXqR z4zeuMuKQvbMx`<g!&a+ByX)0(ILxxF?3>MIfApg;8jXD49}EVxLjV8(0Pu?*?<zjx Tbod9d00000NkvXXu0mjfVtKvc literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs/dialogs_verified_star.png b/Telegram/Resources/icons/dialogs/dialogs_verified_star.png new file mode 100644 index 0000000000000000000000000000000000000000..334e70cdf53a3d0fe553991bf42e12324c0de1cc GIT binary patch literal 371 zcmV-(0gV2MP)<h;3K|Lk000e1NJLTq000sI000mO1^@s68wM|200009a7bBm000XU z000XU0RWnu7ytkPB}qg<R5*>TQoE{yKos0-ZXx&?Lad@Tf}d#GfcOJqZNNXUNs&Se zi=<1fkPx47v(Bv)<*G{{0}E$i7|zU@A3_Mo7a)8?<Rc-+amaqZU*%x#iJ~~nhGBp) zhPG{?s_HCfR!EX0FvbwaF^<P0s;WYdJkR0#K9VFs(=-qSVOfX}g3ILs9>Opj_=_1` zuUBqvx7%$!Bhxe|c9!sXJfLaXyfxdl!5HI;^q`c&vaJ8silQLz_nT|02c?vhWjSwc zx7*?Qd~&zRvJBhpcKvEW5O9TD*X3VLQ53E%S(YdN*;c<^FPu&%c%Fy*{XT4wrYStn z!})whQ54v0HtW_#jfJl3pp>Gn>v1L%&hOINUDpkB6h-84IFPa|Cq*4|{s1Mjrz;z6 ROLqVO002ovPDHLkV1kqTo(BK` literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs/dialogs_verified_star@2x.png b/Telegram/Resources/icons/dialogs/dialogs_verified_star@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9bfd666d5c160eb6c5bd3e7c6290aa2218440707 GIT binary patch literal 714 zcmV;*0yX`KP)<h;3K|Lk000e1NJLTq001Na001Be1^@s6dXd_W00009a7bBm000XU z000XU0RWnu7ytkQd`Uz>R9J=WmN9E8K@i9Pcbbce91?`f6<)6q`~-n~gZm1W0l_A1 zI;)f+jYSFrDz+NM<Or5_hhRt(B`K2>VlII*7{k0uLGR4Dy7FG}{xHq3JNsjHc6I~+ zgnvll-v(IJe;_Y$2a;tO-EJ4HRtu6OtrdfK+S%C|O_j}N*NMRoMo|>{{QR5&y}rJZ zEX&&k(lm_}MRE9dcX#RL=7tt^b#+CeC_2{1Vlh%x)!`SWRI5nSG(0^$!RPbAG)-Jz zU*qxd5qo=kI6gi`GMRMU*KW5_C=_7ZHj>FCbX`Y09*1q)h(@CrjYf0pSI&;As*Vh7 zqOY$n^85XBXE438mzNiGIvtiRzv}k(7L&<j&d2=htJNwupjxfw@G%4R`+Y7j%VIGD z?eFh%fdK$TQC$5i7quiwbbo)(t*Gzr?nn@X_4<&eX|8={Q<X|3ibNu-0|kRY%H?uo z+ctNYsRn}qot&JIBuU;tola+KAd9Nk>)w6HvaFQ~l!rb(J^*-RXlG}Kve_)Xy}fNM zGreAq(&_Z{&s(ji!{IPhDi!W9=jZ3s+F}3geLf$VrpXPaUavdOU}}s%KR+lG3aq8E zRF{{R4j*%;9u9|GU;yC#{oUbX20A!6;K2d_NF)-jex`}>;o*T>4H<^vb_Uz{%2VU< zc=3gipC!fL-`^OGMi2x6hGEP`SXEV#$z*VNc<8#X(P$u_&%?4Tq*5v9x{jlxBUqLN zK@c{(vQQ}G7&`%wD2h}rmmOKSxVTt4IRpZM&92PLWLc)cU@(Juetuqd=x@dk2mqa) wp3Z>M>GV1=_yPq20ctjzR4f+VhA&V1147-R1(!1{r2qf`07*qoM6N<$f=up0c>n+a literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs/dialogs_verified_star@3x.png b/Telegram/Resources/icons/dialogs/dialogs_verified_star@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bcfd5eeb77a6436353e5dd1f69af6f33dad3a78b GIT binary patch literal 803 zcmV+;1Kj+HP)<h;3K|Lk000e1NJLTq001@s001xu0ssI2)qWnA00009a7bBm000XU z000XU0RWnu7ytkQ)k#D_R9J=WnNMo+Kor1V{!ll@l}ZI8PH|y(b|FX?9>7F!Bcv$a zpzb_|$LO+RTU*z90uP|5Db#=qt%XubCg~!45Tjx;W~zMjw;bY|UuND+W}X0l{PD;C z14lJ_AP`U#C7n)>Mk9<d)xJn56biboo2F@+rmpLSLgCm(>qH_EP18&pnx;h}kz;%P zgX6ePr(<r=>2x@bZfdzg5QO{t`%OQKR;wilf~SCl@03g?Yqi?-^>sKL{`&fQe}4x6 zgiy6wy}P^1<#M}zczJnQsZ=n=^Z9(aTmpb7iurtAmgQ_VJD<-Niv^j|8VI4)YGofA z!!R($yGCvo7-PdQ>@BO+3L)g^jwMhm7K@I_Vi<;HJ%?ghmSGrsiy#QaV$lahswux2 zoJ4Ur9Qq+cGq@;<UenG!q9{7LW421{1!GK*e0wnr!=H%;0Mj%N_Hi4QL=hoGX~l3H zmq;WWjU<Y)Ec@ZJol>cEXoz4iIGIf7EtlzZ>h%k`2FG!&R*RmWMZ4W5Or&Tu+U<5} z`&qoayhNi>Z+?;_InIBy(KJnxByhPeo}Hb2et!P`Q4eFRD2l5`6-D{Y@w4;x_U3BS z`TqVsXfzH0V|?lpeRFf;_UR)o7Z04DpZ9vb-|_4B`>|Ne>!<?&@I2pcw`u!5JUs9` zPvU1G2m;j)psK0|eE=<PZ*S=-*6Z~{{Qv+VL{D+^21t17)^(l69)vO8%tFFbx8ri@ z7hA)UD2k#;<KhAUEX&5@ac3hzpiCy?gU$}FuC5#{B#Nn2$`2X0!ATUU)>{q3@F5C9 z=;PzVzEe;wI}t9-i;D|cmU*5Z4u^xmKvh*sU6Q0mqp^9>w4<tOv)O!ldK!<%0FX!| zGMP*&mC|+n@$qponUE=YU~dUD6bez7*vqa$^7HfaNfeSTPQ4s&AxY9n|EXb7E|+Js h+3V|Txm+d=@&g0~sRbUb$Ql3u002ovPDHLkV1iYNYrX&g literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/dialogs_verified_check.png b/Telegram/Resources/icons/dialogs_verified_check.png deleted file mode 100644 index 67b732e6df1e7604ca2d133aa73a77b91f77cb8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^96-#<!3HENCaw(yQq`U=jv*W~V|!NfvM4gRJZIr7 zGG+76NXS-LD4iAI%6_F`5i^hPgdAoG6~&pt-%mcBH`n|<W5bWf7JN&l1b8tnnX={D zC*4mr{@?EH{GQe`@q}gZInU=Z&Kxn>g*}e%|GrM?d02b@PeAt4Yrmpd7_^q&h<aQa fE8_I0uZHQxdvC{AUHSq*XE1oW`njxgN@xNAC_zLV diff --git a/Telegram/Resources/icons/dialogs_verified_check@2x.png b/Telegram/Resources/icons/dialogs_verified_check@2x.png deleted file mode 100644 index b98a405e0fb7947c5120e88910b8cd2e95539d94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 281 zcmV+!0p|XRP)<h;3K|Lk000e1NJLTq000mG000UI1^@s6y}sU@0002sNkl<ZD3Ohk zugZon7{v!77>6+IX1mQKc8k}r;tdE!ufb^a1}q{NErMm)ZZnA>51cQ2;OpMM9(Z6o z?EJ>g7-I}~$1n^OMS(oeul~KTWm%}}8UR2<h~xM^z;PT5!+@)rrqKam7$Qy6m$2t~ znCDpwS5*bmG#`K<2(WD%h)9D(QG|6}&ma*Y%d+Q77`m?04oN8?rNnU@r@!wzk|cTe zL<Y_|#&OgjS1ro|-}gUt^uMre8`Cu5SxPCmuKTUz$3_SNrPLX2+ZLSjU%#XqLJ0JI fkFqSUO@Ev}wVE;b#K#g#00000NkvXXu0mjf8ZUM; diff --git a/Telegram/Resources/icons/dialogs_verified_check@3x.png b/Telegram/Resources/icons/dialogs_verified_check@3x.png deleted file mode 100644 index ce4145c47bc77ed018c8bd92fe5e0b03ab249503..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 404 zcmV;F0c-w=P)<h;3K|Lk000e1NJLTq000;O000jN0ssI2amJX80000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzHc3Q5RCwBAU?3MnMn<k&xssWgnQRjn z7#SH`T3Y`7`}hC<|7p{vk!cDyH}~4LYyTsG%*;%ZO_7t6JAeNCe^h|(D4<n9H{&tI z($ez9ix<d7{QmtrH#ZkW5f2Z~kt0Xu%$bABtk~GtA3uH|oAU16J2y8s6h&NIT!#)F zf=JJpF$2vkCMKrN&Q3J%+`4s3U0ogBwr$(CAuF0PWeVIZUS8f!n>L}DvTN5aK|w(* z&USTm{r2q}veJnYCj!k<R8+il=@ObLKx<f7Sa1fUySqDD5Ug6Y>h<f_$RfXf{mRJ5 zARM)xo}NE{{``*vynXxD(b19EWa8uFgVn5S*RClmE0dPw0s;ci%-XSI2R}bQIhi0h yI2afl5L5d4`Y6hlp`oF`zzz%yBwh<ZfB^utpFZN;xC@&A0000<MNUMnLSTY?@v-p$ diff --git a/Telegram/Resources/icons/dialogs_verified_star.png b/Telegram/Resources/icons/dialogs_verified_star.png deleted file mode 100644 index 9a4b8467c27059ecb8364b219750ec1cc66010c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 zcmV-c0jd6pP)<h;3K|Lk000e1NJLTq000gE000gM1^@s6A4o0H0003UNkl<ZD3OJd zy{f`M5QT?$i$ESjFjb6=;3Fn&AmjzY))DdsHWn6ISZJ3zLd0bAx31?Fn&5JC*$)PG zc4s(ehan=uR76n(-}fiqKJh-7ra4}&*K0^A(f2);%O$32h=^cW7NnFoole+px2WqH z17%qvNfMmTXS8h#!!T6E%jE(cyWMV7H0Z<idL<oMmMJ+ln~h@cKkoNCT-SZ8hG7UP zrM7862m#OYU{qB_&*xKXb3h0|ob$I;$8qp@JhW=J+YO7wLY-=wrrO3hj{i>8wr#D= z`FyTU)ih0XU5EXC55^dZq8L^3JO^V8hr<C?Rbe)psaC%>0;|;uLI^ZX^Zh5K=4X(7 oy<Ve*<MBvA5D@2FS&Z+RUrI!fJy&Q1CjbBd07*qoM6N<$f{DG5D*ylh diff --git a/Telegram/Resources/icons/dialogs_verified_star@2x.png b/Telegram/Resources/icons/dialogs_verified_star@2x.png deleted file mode 100644 index de65718526dd9a4bd5fb7a2dade840d22fb66089..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 660 zcmV;F0&D$=P)<h;3K|Lk000e1NJLTq000~S000~a1^@s6at+^<00078Nkl<ZNQt$U zKWp+(6vj^y8>5sUBBU9b-8SGSDEJNX6&!^^macA29i@vyhX`%yT1`=dIJyNvacPSk zTZf>rkVvbXcj#+cy?HCX-ac@b!@2jzx##|Q003~CA&MgQdOd8n+sO0$R&)Q3?IxU` zpW{-xuKzAjk|cb4dkcWCudgVI;%-1iQBaa3A~70`Vx!T(byX@AWLcKTKRi4{S(b@7 zv!nt5D2f8Z;Se~E1KYNtR;$6o!viFfNyz8(psFf_snh9zVHn_f9;m7cnx;WIod(bI zAeBmi@B1tH0bQ15qJmvCo6Rs1iL6w}ekZ@Yyg;|x4XxsT)ND3kKA*3|*!85lyE|&Y zYPCwl2;kGx6E$GiS_JUm;D8!XlBCdh&`_S|vET1gJ2b6U3mJw9zlVyVgx<4Vb$xw} ziA3TH7>mWQSS+IFdDLM`IgW#8XJ?;pZ@1h1HPE_D(?r&FU22D9lcv)tz~0^->bj1j z(dgGIdwhJvTrRhKb3dE8zrT;Sx3|<`rBZ4A-~2kV9LJ$;+tgsDX%fH1%EezS7GM|# zwVqvGUJ^0lr5TUM)PR%8WHa#i_?Q~-=;$akzOu)I!GKy1EzA1ZVknA24TgyL{{Bul zyU6!aKR!Od_kCa(1}w`8EQT!0aC&+QCnqPNTXcVa59M+hT-Sw6CIgzLK{lHO*L8tm zn4h;a9*+~Y0059>S-iQqA*#5zxCnJ;JkO&b2)o%tQ54Z}oB;Ox{QR#)>G)Crz(S!A u0CTzAwy^Dif*@eMUdOAetFX^?<M;zQL^WxJ9EtV-0000<MNUMnLSTXiNIPx- diff --git a/Telegram/Resources/icons/dialogs_verified_star@3x.png b/Telegram/Resources/icons/dialogs_verified_star@3x.png deleted file mode 100644 index 22b94f48389899818f3a8770ffa41d67e57d8ad4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 694 zcmV;n0!jUeP)<h;3K|Lk000e1NJLTq001fg001fo0ssI2N}*m10000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!SV=@dRCwCNSj(;{K@{z7h8Q6v2x4N! zKq3Y{z$1}hMEn6WpW!nMiMKId-~%LH2@(UZi2?eod!3vkUF}xL-Q8={wYzGs>Z)D4 zR&PQ06xnQcv)QaxD?A7qBauj4uUGjm@PMXv+%}tSyWPr9KodqSxD5tFuh)|qz=&70 z+-9@6(P+po&}=r*;k}PxO<u3JR4T<{F{{;jxm=FNBaA+uuUIVB>-A_f`pQ=@7%UVD zx7%&E+Z_%ESln)RJRVP_Qjtgm#{GWJnGR@9r_=K{-S2mJl-~q~VBoU_ZsA-(Cn?(L zbXu{1q9{s|^zJrc2nH%P4$e9Bq~XIlSJF?XQ#yLSa|DY_1~^yHiR=szH6Ls?7l=gu z;scl_#mcL?mDqzv6n${(@pw4&ND*|Qm&s)Q_1a`I&F6Ev&|54PFS9Zh2T5BdMyu6Q z*<8EbJ{%5ZO)wY?K=;d?OePigH#I;9(m&j}T<%A=XbF&lT3oN!PN!4Q1CWXmW+CVV zq*o!I&+E1Lpw>I|9&k7uqtQqk?(ul65^YAK5jlaHyWMVs=%tV)O}fkFk|Tx7<uV<8 zrBdN5T{?PJ>8hw590`gmAE%t%brrRv5Xk=vs%|A#YElTjhw^v1TpapPC`6ZK!{IQ8 zJ`f1d(X-Aam1}?u(AxZicRrsr`^8j=M!(+=y^BMFf1=4`f>iaG7A}cuwaSX+XP|Sf zR_k`Vv)K$ybVe{?&2gAJoeokeZF#|BvFKA=>G%8O#TETaOF!aRx5C;3kV=oT;<sk{ c2wwsW0Qkxnnpi&oGynhq07*qoM6N<$g6;D-ssI20 diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 4b7cf1b1c..cc5322673 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -101,8 +101,8 @@ dialogsMenuToggle: IconButton { width: 40px; height: 40px; - icon: icon {{ "dialogs_menu", dialogsMenuIconFg }}; - iconOver: icon {{ "dialogs_menu", dialogsMenuIconFgOver }}; + icon: icon {{ "dialogs/dialogs_menu", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs/dialogs_menu", dialogsMenuIconFgOver }}; iconPosition: point(-1px, -1px); rippleAreaPosition: point(0px, 0px); @@ -112,32 +112,32 @@ dialogsMenuToggle: IconButton { } } dialogsMenuToggleUnread: icon { - { "dialogs_menu_unread", dialogsMenuIconFg }, - { "dialogs_menu_unread_dot", dialogsUnreadBg }, + { "dialogs/dialogs_menu_unread", dialogsMenuIconFg }, + { "dialogs/dialogs_menu_unread_dot", dialogsUnreadBg }, }; dialogsMenuToggleUnreadMuted: icon { - { "dialogs_menu_unread", dialogsMenuIconFg }, - { "dialogs_menu_unread_dot", dialogsMenuIconFg }, + { "dialogs/dialogs_menu_unread", dialogsMenuIconFg }, + { "dialogs/dialogs_menu_unread_dot", dialogsMenuIconFg }, }; dialogsLock: IconButton(dialogsMenuToggle) { - icon: icon {{ "dialogs_lock", dialogsMenuIconFg }}; - iconOver: icon {{ "dialogs_lock", dialogsMenuIconFgOver }}; + icon: icon {{ "dialogs/dialogs_lock", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs/dialogs_lock", dialogsMenuIconFgOver }}; } -dialogsUnlockIcon: icon {{ "dialogs_unlock", dialogsMenuIconFg }}; -dialogsUnlockIconOver: icon {{ "dialogs_unlock", dialogsMenuIconFgOver }}; +dialogsUnlockIcon: icon {{ "dialogs/dialogs_unlock", dialogsMenuIconFg }}; +dialogsUnlockIconOver: icon {{ "dialogs/dialogs_unlock", dialogsMenuIconFgOver }}; dialogsCalendar: IconButton { width: 29px; height: 32px; - icon: icon {{ "dialogs_calendar", dialogsMenuIconFg }}; - iconOver: icon {{ "dialogs_calendar", dialogsMenuIconFgOver }}; + icon: icon {{ "dialogs/dialogs_calendar", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs/dialogs_calendar", dialogsMenuIconFgOver }}; iconPosition: point(0px, 5px); } dialogsSearchFrom: IconButton(dialogsCalendar) { width: 26px; - icon: icon {{ "dialogs_search_from", dialogsMenuIconFg }}; - iconOver: icon {{ "dialogs_search_from", dialogsMenuIconFgOver }}; + icon: icon {{ "dialogs/dialogs_search_from", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs/dialogs_search_from", dialogsMenuIconFgOver }}; } dialogsSearchForNarrowFilters: IconButton(dialogsMenuToggle) { icon: icon {{ "top_bar_search", menuIconFg }}; @@ -154,8 +154,8 @@ dialogsFilter: FlatInput(defaultFlatInput) { textMrg: margins(12px, 3px, 30px, 3px); } dialogsCancelSearchInPeer: IconButton(dialogsMenuToggle) { - icon: icon {{ "dialogs_cancel_search", dialogsMenuIconFg }}; - iconOver: icon {{ "dialogs_cancel_search", dialogsMenuIconFgOver }}; + icon: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFg }}; + iconOver: icon {{ "dialogs/dialogs_cancel_search", dialogsMenuIconFgOver }}; iconPosition: point(11px, 11px); rippleAreaPosition: point(3px, 3px); rippleAreaSize: 34px; @@ -180,49 +180,49 @@ dialogsCancelSearch: CrossButton { } dialogsChatTypeSkip: 22px; -dialogsChatIcon: icon {{ "dialogs_chat", dialogsChatIconFg, point(1px, 4px) }}; -dialogsChatIconOver: icon {{ "dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }}; -dialogsChatIconActive: icon {{ "dialogs_chat", dialogsChatIconFgActive, point(1px, 4px) }}; -dialogsChannelIcon: icon {{ "dialogs_channel", dialogsChatIconFg, point(3px, 4px) }}; -dialogsChannelIconOver: icon {{ "dialogs_channel", dialogsChatIconFgOver, point(3px, 4px) }}; -dialogsChannelIconActive: icon {{ "dialogs_channel", dialogsChatIconFgActive, point(3px, 4px) }}; -dialogsBotIcon: icon {{ "dialogs_bot", dialogsChatIconFg, point(1px, 3px) }}; -dialogsBotIconOver: icon {{ "dialogs_bot", dialogsChatIconFgOver, point(1px, 3px) }}; -dialogsBotIconActive: icon {{ "dialogs_bot", dialogsChatIconFgActive, point(1px, 3px) }}; +dialogsChatIcon: icon {{ "dialogs/dialogs_chat", dialogsChatIconFg, point(1px, 4px) }}; +dialogsChatIconOver: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgOver, point(1px, 4px) }}; +dialogsChatIconActive: icon {{ "dialogs/dialogs_chat", dialogsChatIconFgActive, point(1px, 4px) }}; +dialogsChannelIcon: icon {{ "dialogs/dialogs_channel", dialogsChatIconFg, point(3px, 4px) }}; +dialogsChannelIconOver: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgOver, point(3px, 4px) }}; +dialogsChannelIconActive: icon {{ "dialogs/dialogs_channel", dialogsChatIconFgActive, point(3px, 4px) }}; +dialogsBotIcon: icon {{ "dialogs/dialogs_bot", dialogsChatIconFg, point(1px, 3px) }}; +dialogsBotIconOver: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgOver, point(1px, 3px) }}; +dialogsBotIconActive: icon {{ "dialogs/dialogs_bot", dialogsChatIconFgActive, point(1px, 3px) }}; dialogsArchiveUserpic: icon {{ "archive_userpic", historyPeerUserpicFg }}; dialogsRepliesUserpic: icon {{ "replies_userpic", historyPeerUserpicFg }}; dialogsSendStateSkip: 20px; -dialogsSendingIcon: icon {{ "dialogs_sending", dialogsSendingIconFg, point(8px, 4px) }}; -dialogsSendingIconOver: icon {{ "dialogs_sending", dialogsSendingIconFgOver, point(8px, 4px) }}; -dialogsSendingIconActive: icon {{ "dialogs_sending", dialogsSendingIconFgActive, point(8px, 4px) }}; -dialogsSentIcon: icon {{ "dialogs_sent", dialogsSentIconFg, point(10px, 4px) }}; -dialogsSentIconOver: icon {{ "dialogs_sent", dialogsSentIconFgOver, point(10px, 4px) }}; -dialogsSentIconActive: icon {{ "dialogs_sent", dialogsSentIconFgActive, point(10px, 4px) }}; -dialogsReceivedIcon: icon {{ "dialogs_received", dialogsSentIconFg, point(5px, 4px) }}; -dialogsReceivedIconOver: icon {{ "dialogs_received", dialogsSentIconFgOver, point(5px, 4px) }}; -dialogsReceivedIconActive: icon {{ "dialogs_received", dialogsSentIconFgActive, point(5px, 4px) }}; -dialogsPinnedIcon: icon {{ "dialogs_pinned", dialogsUnreadBgMuted }}; -dialogsPinnedIconOver: icon {{ "dialogs_pinned", dialogsUnreadBgMutedOver }}; -dialogsPinnedIconActive: icon {{ "dialogs_pinned", dialogsUnreadBgMutedActive }}; +dialogsSendingIcon: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFg, point(8px, 4px) }}; +dialogsSendingIconOver: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgOver, point(8px, 4px) }}; +dialogsSendingIconActive: icon {{ "dialogs/dialogs_sending", dialogsSendingIconFgActive, point(8px, 4px) }}; +dialogsSentIcon: icon {{ "dialogs/dialogs_sent", dialogsSentIconFg, point(10px, 4px) }}; +dialogsSentIconOver: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgOver, point(10px, 4px) }}; +dialogsSentIconActive: icon {{ "dialogs/dialogs_sent", dialogsSentIconFgActive, point(10px, 4px) }}; +dialogsReceivedIcon: icon {{ "dialogs/dialogs_received", dialogsSentIconFg, point(5px, 4px) }}; +dialogsReceivedIconOver: icon {{ "dialogs/dialogs_received", dialogsSentIconFgOver, point(5px, 4px) }}; +dialogsReceivedIconActive: icon {{ "dialogs/dialogs_received", dialogsSentIconFgActive, point(5px, 4px) }}; +dialogsPinnedIcon: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMuted }}; +dialogsPinnedIconOver: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedOver }}; +dialogsPinnedIconActive: icon {{ "dialogs/dialogs_pinned", dialogsUnreadBgMutedActive }}; dialogsVerifiedIcon: icon { - { "dialogs_verified_star", dialogsVerifiedIconBg, point(4px, 2px) }, - { "dialogs_verified_check", dialogsVerifiedIconFg, point(7px, 7px) }, + { "dialogs/dialogs_verified_star", dialogsVerifiedIconBg }, + { "dialogs/dialogs_verified_check", dialogsVerifiedIconFg }, }; dialogsVerifiedIconOver: icon { - { "dialogs_verified_star", dialogsVerifiedIconBgOver, point(4px, 2px) }, - { "dialogs_verified_check", dialogsVerifiedIconFgOver, point(7px, 7px) }, + { "dialogs/dialogs_verified_star", dialogsVerifiedIconBgOver }, + { "dialogs/dialogs_verified_check", dialogsVerifiedIconFgOver }, }; dialogsVerifiedIconActive: icon { - { "dialogs_verified_star", dialogsVerifiedIconBgActive, point(4px, 2px) }, - { "dialogs_verified_check", dialogsVerifiedIconFgActive, point(7px, 7px) }, + { "dialogs/dialogs_verified_star", dialogsVerifiedIconBgActive }, + { "dialogs/dialogs_verified_check", dialogsVerifiedIconFgActive }, }; -historySendingIcon: icon {{ "dialogs_sending", historySendingOutIconFg, point(5px, 5px) }}; -historySendingInvertedIcon: icon {{ "dialogs_sending", historySendingInvertedIconFg, point(5px, 5px) }}; -historyViewsSendingIcon: icon {{ "dialogs_sending", historySendingInIconFg, point(3px, 0px) }}; -historyViewsSendingInvertedIcon: icon {{ "dialogs_sending", historySendingInvertedIconFg, point(3px, 0px) }}; +historySendingIcon: icon {{ "dialogs/dialogs_sending", historySendingOutIconFg, point(5px, 5px) }}; +historySendingInvertedIcon: icon {{ "dialogs/dialogs_sending", historySendingInvertedIconFg, point(5px, 5px) }}; +historyViewsSendingIcon: icon {{ "dialogs/dialogs_sending", historySendingInIconFg, point(3px, 0px) }}; +historyViewsSendingInvertedIcon: icon {{ "dialogs/dialogs_sending", historySendingInvertedIconFg, point(3px, 0px) }}; dialogsUpdateButton: FlatButton { color: activeButtonFg; diff --git a/Telegram/SourceFiles/window/window.style b/Telegram/SourceFiles/window/window.style index 854c5df4b..a9fb195c7 100644 --- a/Telegram/SourceFiles/window/window.style +++ b/Telegram/SourceFiles/window/window.style @@ -281,17 +281,17 @@ windowFiltersButton: SideBarButton(defaultSideBarButton) { iconPosition: point(-1px, 6px); } windowFiltersMainMenu: SideBarButton(windowFiltersButton) { - icon: icon {{ "dialogs_menu", sideBarIconFg }}; + icon: icon {{ "dialogs/dialogs_menu", sideBarIconFg }}; iconPosition: point(-1px, -1px); minHeight: 54px; } windowFiltersMainMenuUnread: icon { - { "dialogs_menu_unread", sideBarIconFg }, - { "dialogs_menu_unread_dot", sideBarBadgeBg }, + { "dialogs/dialogs_menu_unread", sideBarIconFg }, + { "dialogs/dialogs_menu_unread_dot", sideBarBadgeBg }, }; windowFiltersMainMenuUnreadMuted: icon { - { "dialogs_menu_unread", sideBarIconFg }, - { "dialogs_menu_unread_dot", sideBarBadgeBgMuted }, + { "dialogs/dialogs_menu_unread", sideBarIconFg }, + { "dialogs/dialogs_menu_unread_dot", sideBarBadgeBgMuted }, }; windowFilterSmallItem: PeerListItem(defaultPeerListItem) { height: 44px; From 6db537d1ec9c6cf44ee936e6a9194f1baa23fadf Mon Sep 17 00:00:00 2001 From: Ilya Fedin <fedin-ilja2010@ya.ru> Date: Sun, 5 Dec 2021 23:06:36 +0400 Subject: [PATCH 174/180] Rename telegramdesktop.appdata.xml -> telegramdesktop.metainfo.xml .appinfo.xml is legacy according to https://freedesktop.org/software/appstream/docs/chap-Metadata.html#spec-component-location --- Telegram/CMakeLists.txt | 6 +++--- Telegram/build/changelog2appdata.py | 2 +- ...sktop.appdata.xml.in => telegramdesktop.metainfo.xml.in} | 0 snap/snapcraft.yaml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename lib/xdg/{telegramdesktop.appdata.xml.in => telegramdesktop.metainfo.xml.in} (100%) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 3dc2e6211..2cfb5eca1 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1526,8 +1526,8 @@ endif() if (LINUX AND DESKTOP_APP_USE_PACKAGED) include(GNUInstallDirs) - configure_file("../lib/xdg/telegramdesktop.appdata.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml" @ONLY) - generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml") + configure_file("../lib/xdg/telegramdesktop.metainfo.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" @ONLY) + generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml") install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}") install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "telegram.png") install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "telegram.png") @@ -1537,5 +1537,5 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED) install(FILES "Resources/art/icon256.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps" RENAME "telegram.png") install(FILES "Resources/art/icon512.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps" RENAME "telegram.png") install(FILES "../lib/xdg/telegramdesktop.desktop" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.desktop") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.appdata.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.appdata.xml") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/telegramdesktop.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME "${TDESKTOP_LAUNCHER_BASENAME}.metainfo.xml") endif() diff --git a/Telegram/build/changelog2appdata.py b/Telegram/build/changelog2appdata.py index d97817eaa..4ba34b677 100755 --- a/Telegram/build/changelog2appdata.py +++ b/Telegram/build/changelog2appdata.py @@ -62,7 +62,7 @@ def update_appdata(appdata_path, changelog, max_items=None): def main(): ap = argparse.ArgumentParser("Parse Telegram changelog") ap.add_argument("-c", "--changelog-path", default="changelog.txt") - ap.add_argument("-a", "--appdata-path", default="lib/xdg/telegramdesktop.appdata.xml") + ap.add_argument("-a", "--appdata-path", default="lib/xdg/telegramdesktop.metainfo.xml") ap.add_argument("-n", "--num-releases", type=int, default=None) args = ap.parse_args() update_appdata(args.appdata_path, diff --git a/lib/xdg/telegramdesktop.appdata.xml.in b/lib/xdg/telegramdesktop.metainfo.xml.in similarity index 100% rename from lib/xdg/telegramdesktop.appdata.xml.in rename to lib/xdg/telegramdesktop.metainfo.xml.in diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index e04c866e3..dd7414550 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -85,7 +85,7 @@ parts: plugin: cmake source: . source-type: git - parse-info: [usr/share/metainfo/telegram-desktop_telegram-desktop.appdata.xml] + parse-info: [usr/share/metainfo/telegram-desktop_telegram-desktop.metainfo.xml] build-environment: - CC: gcc-10 - CXX: g++-10 From 01c2be3f0139fc237e6286bd077d125bcaed10df Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 6 Dec 2021 12:31:08 +0400 Subject: [PATCH 175/180] Add some checks for actions in a locked state. --- .../dialogs/dialogs_inner_widget.cpp | 5 ++++- Telegram/SourceFiles/mainwidget.cpp | 8 ++++++-- .../platform/mac/main_window_mac.mm | 18 +++++++++--------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 7fdceb59f..5c575dddf 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "main/main_session_settings.h" #include "window/notifications_manager.h" +#include "window/window_controller.h" #include "window/window_session_controller.h" #include "window/window_peer_menu.h" #include "ui/widgets/multi_select.h" @@ -3011,7 +3012,9 @@ void InnerWidget::updateRowCornerStatusShown( void InnerWidget::setupShortcuts() { Shortcuts::Requests( ) | rpl::filter([=] { - return isActiveWindow() && !Ui::isLayerShown(); + return isActiveWindow() + && !Ui::isLayerShown() + && !_controller->window().locked(); }) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) { using Command = Shortcuts::Command; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index a36fbea4a..79e97caaa 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1257,8 +1257,9 @@ void MainWidget::ui_showPeerHistory( PeerId peerId, const SectionShow ¶ms, MsgId showAtMsgId) { - - if (auto peer = session().data().peerLoaded(peerId)) { + if (peerId && _controller->window().locked()) { + return; + } else if (auto peer = session().data().peerLoaded(peerId)) { if (peer->migrateTo()) { peer = peer->migrateTo(); peerId = peer->id; @@ -1579,6 +1580,9 @@ void MainWidget::showNewSection( const SectionShow ¶ms) { using Column = Window::Column; + if (_controller->window().locked()) { + return; + } auto saveInStack = (params.way == SectionShow::Way::Forward); const auto thirdSectionTop = getThirdSectionTop(); const auto newThirdGeometry = QRect( diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index c9ee23ddd..e9092ca4e 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -485,17 +485,15 @@ void MainWindow::createGlobalMenu() { QMenu *window = psMainMenu.addMenu(tr::lng_mac_menu_window(tr::now)); psContacts = window->addAction(tr::lng_mac_menu_contacts(tr::now)); connect(psContacts, &QAction::triggered, psContacts, crl::guard(this, [=] { - if (isHidden()) { - showFromTray(); - } - if (!sessionController()) { - return; - } + Expects(sessionController() != nullptr && !controller().locked()); + + ensureWindowShown(); sessionController()->show(PrepareContactsBox(sessionController())); })); { auto callback = [=] { - Expects(sessionController() != nullptr); + Expects(sessionController() != nullptr && !controller().locked()); + ensureWindowShown(); sessionController()->showAddContact(); }; @@ -507,7 +505,8 @@ void MainWindow::createGlobalMenu() { window->addSeparator(); { auto callback = [=] { - Expects(sessionController() != nullptr); + Expects(sessionController() != nullptr && !controller().locked()); + ensureWindowShown(); sessionController()->showNewGroup(); }; @@ -518,7 +517,8 @@ void MainWindow::createGlobalMenu() { } { auto callback = [=] { - Expects(sessionController() != nullptr); + Expects(sessionController() != nullptr && !controller().locked()); + ensureWindowShown(); sessionController()->showNewChannel(); }; From d199e16a6e90747fa034eb3d31105d4f7d26ffba Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 6 Dec 2021 15:00:17 +0400 Subject: [PATCH 176/180] Load cloud image without active view only once. --- Telegram/SourceFiles/data/data_cloud_file.cpp | 9 ++++++++- Telegram/SourceFiles/data/data_cloud_file.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/data/data_cloud_file.cpp b/Telegram/SourceFiles/data/data_cloud_file.cpp index aa5bfa6c3..f0801e13e 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.cpp +++ b/Telegram/SourceFiles/data/data_cloud_file.cpp @@ -99,13 +99,19 @@ bool CloudImage::failed() const { return (_file.flags & CloudFile::Flag::Failed); } +bool CloudImage::loadedOnce() const { + return (_file.flags & CloudFile::Flag::Loaded); +} + void CloudImage::load(not_null<Main::Session*> session, FileOrigin origin) { const auto autoLoading = false; const auto finalCheck = [=] { if (const auto active = activeView()) { return !active->image(); + } else if (_file.flags & CloudFile::Flag::Loaded) { + return false; } - return true; + return !(_file.flags & CloudFile::Flag::Loaded); }; const auto done = [=](QImage result) { if (const auto active = activeView()) { @@ -247,6 +253,7 @@ void LoadCloudFile( if (!file.loader || file.loader->cancelled()) { file.flags |= CloudFile::Flag::Cancelled; } else { + file.flags |= CloudFile::Flag::Loaded; done(file); } // NB! file.loader may be in ~FileLoader() already. diff --git a/Telegram/SourceFiles/data/data_cloud_file.h b/Telegram/SourceFiles/data/data_cloud_file.h index a1ccc7afe..56a5e250b 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.h +++ b/Telegram/SourceFiles/data/data_cloud_file.h @@ -31,6 +31,7 @@ struct CloudFile final { enum class Flag : uchar { Cancelled = 0x01, Failed = 0x02, + Loaded = 0x04, }; friend inline constexpr bool is_flag_type(Flag) { return true; }; @@ -73,6 +74,7 @@ public: [[nodiscard]] bool empty() const; [[nodiscard]] bool loading() const; [[nodiscard]] bool failed() const; + [[nodiscard]] bool loadedOnce() const; void load(not_null<Main::Session*> session, FileOrigin origin); [[nodiscard]] const ImageLocation &location() const; [[nodiscard]] int byteSize() const; From e3b9927faad1801baf8782855d6008ad58eab086 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 7 Dec 2021 10:01:45 +0400 Subject: [PATCH 177/180] Reset session on receiving a really old msgId. --- .../details/mtproto_received_ids_manager.cpp | 19 ++++++++++--------- .../details/mtproto_received_ids_manager.h | 7 ++++++- .../SourceFiles/mtproto/session_private.cpp | 14 ++++++++++++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.cpp b/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.cpp index 28e6ad518..20bade0c4 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.cpp +++ b/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.cpp @@ -9,18 +9,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace MTP::details { -bool ReceivedIdsManager::registerMsgId(mtpMsgId msgId, bool needAck) { +ReceivedIdsManager::Result ReceivedIdsManager::registerMsgId( + mtpMsgId msgId, + bool needAck) { const auto i = _idsNeedAck.find(msgId); - if (i == _idsNeedAck.end()) { - if (_idsNeedAck.size() < kIdsBufferSize || msgId > min()) { - _idsNeedAck.emplace(msgId, needAck); - return true; - } - MTP_LOG(-1, ("No need to handle - %1 < min = %2").arg(msgId).arg(min())); - } else { + if (i != _idsNeedAck.end()) { MTP_LOG(-1, ("No need to handle - %1 already is in map").arg(msgId)); + return Result::Duplicate; + } else if (_idsNeedAck.size() < kIdsBufferSize || msgId > min()) { + _idsNeedAck.emplace(msgId, needAck); + return Result::Success; } - return false; + MTP_LOG(-1, ("Reset on too old - %1 < min = %2").arg(msgId).arg(min())); + return Result::TooOld; } mtpMsgId ReceivedIdsManager::min() const { diff --git a/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.h b/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.h index 2a8d6a57a..8f7dfbb9d 100644 --- a/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.h +++ b/Telegram/SourceFiles/mtproto/details/mtproto_received_ids_manager.h @@ -21,8 +21,13 @@ public: NeedsAck, NoAckNeeded, }; + enum class Result { + Success, + Duplicate, + TooOld, + }; - bool registerMsgId(mtpMsgId msgId, bool needAck); + [[nodiscard]] Result registerMsgId(mtpMsgId msgId, bool needAck); [[nodiscard]] mtpMsgId min() const; [[nodiscard]] mtpMsgId max() const; [[nodiscard]] State lookup(mtpMsgId msgId) const; diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index 0bd74787a..ca46f113e 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -1364,13 +1364,18 @@ void SessionPrivate::handleReceived() { ).arg(getProtocolDcId() ).arg(_encryptionKey->keyId())); - if (_receivedMessageIds.registerMsgId(msgId, needAck)) { + const auto registered = _receivedMessageIds.registerMsgId( + msgId, + needAck); + if (registered == ReceivedIdsManager::Result::Success) { res = handleOneReceived(from, end, msgId, { .outerMsgId = msgId, .serverSalt = serverSalt, .serverTime = serverTime, .badTime = badTime, }); + } else if (registered == ReceivedIdsManager::Result::TooOld) { + res = HandleResult::ResetSession; } _receivedMessageIds.shrink(); @@ -1478,9 +1483,14 @@ SessionPrivate::HandleResult SessionPrivate::handleOneReceived( } auto res = HandleResult::Success; // if no need to handle, then succeed - if (_receivedMessageIds.registerMsgId(inMsgId.v, needAck)) { + const auto registered = _receivedMessageIds.registerMsgId( + inMsgId.v, + needAck); + if (registered == ReceivedIdsManager::Result::Success) { res = handleOneReceived(from, otherEnd, inMsgId.v, info); info.badTime = false; + } else if (registered == ReceivedIdsManager::Result::TooOld) { + res = HandleResult::ResetSession; } if (res != HandleResult::Success) { return res; From 1a0d4302914f03f027e68aaadf537fe98dc4630c Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 7 Dec 2021 15:41:55 +0400 Subject: [PATCH 178/180] Fix crash in admin log right click. Fixes #17325. --- .../history/admin_log/history_admin_log_inner.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index f226f70f3..9662744ce 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -1160,8 +1160,9 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false; auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false; auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false; - const auto fromId = PeerId( - link->property(kPeerLinkPeerIdProperty).toULongLong()); + const auto fromId = PeerId(link + ? link->property(kPeerLinkPeerIdProperty).toULongLong() + : 0); if (lnkPhoto || lnkDocument) { if (isUponSelected > 0) { _menu->addAction(tr::lng_context_copy_selected(tr::now), [=] { From f6d29991d633b2175882d04d5aa55642c898636e Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 7 Dec 2021 15:52:33 +0400 Subject: [PATCH 179/180] Version 3.3. - Content creators can restrict the ability to save media and forward messages from their groups and channels. - Clear messages in one-on-one chats from a specific day or date range. - Comment as one of your channels in public groups and channel comments. - When you request to join a community and its admin or bot-admin contacts you with a message, you will see which chat they are from at the top of the chat. - Bot-admins can now ask users to complete tasks before they are allowed to join - like accepting community rules, passing a test, or making a donation to the content creators. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 6 +++--- Telegram/build/version | 12 ++++++------ changelog.txt | 8 ++++++++ 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 28ac48d80..9e4755846 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ <Identity Name="TelegramMessengerLLP.TelegramDesktop" ProcessorArchitecture="ARCHITECTURE" Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A" - Version="3.2.8.0" /> + Version="3.3.0.0" /> <Properties> <DisplayName>Telegram Desktop</DisplayName> <PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName> diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 452a1d6db..3e06cabaf 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 3,2,8,0 - PRODUCTVERSION 3,2,8,0 + FILEVERSION 3,3,0,0 + PRODUCTVERSION 3,3,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "3.2.8.0" + VALUE "FileVersion", "3.3.0.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.8.0" + VALUE "ProductVersion", "3.3.0.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index f5127aa9b..7e621db0b 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 3,2,8,0 - PRODUCTVERSION 3,2,8,0 + FILEVERSION 3,3,0,0 + PRODUCTVERSION 3,3,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "3.2.8.0" + VALUE "FileVersion", "3.3.0.0" VALUE "LegalCopyright", "Copyright (C) 2014-2021" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "3.2.8.0" + VALUE "ProductVersion", "3.3.0.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 5242e78e7..2f40be903 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -22,7 +22,7 @@ constexpr auto AppId = "{53F49750-6209-4FBF-9CA8-7A333C87D1ED}"_cs; constexpr auto AppNameOld = "Telegram Win (Unofficial)"_cs; constexpr auto AppName = "Telegram Desktop"_cs; constexpr auto AppFile = "Telegram"_cs; -constexpr auto AppVersion = 3002008; -constexpr auto AppVersionStr = "3.2.8"; -constexpr auto AppBetaVersion = true; +constexpr auto AppVersion = 3003000; +constexpr auto AppVersionStr = "3.3"; +constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 844ce9a9c..f8102b229 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 3002008 -AppVersionStrMajor 3.2 -AppVersionStrSmall 3.2.8 -AppVersionStr 3.2.8 -BetaChannel 1 +AppVersion 3003000 +AppVersionStrMajor 3.3 +AppVersionStrSmall 3.3 +AppVersionStr 3.3.0 +BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 3.2.8.beta +AppVersionOriginal 3.3 diff --git a/changelog.txt b/changelog.txt index e08b73a8e..cecace3f0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,11 @@ +3.3 (07.12.21) + +- Content creators can restrict the ability to save media and forward messages from their groups and channels. +- Clear messages in one-on-one chats from a specific day or date range. +- Comment as one of your channels in public groups and channel comments. +- When you request to join a community and its admin or bot-admin contacts you with a message, you will see which chat they are from at the top of the chat. +- Bot-admins can now ask users to complete tasks before they are allowed to join - like accepting community rules, passing a test, or making a donation to the content creators. + 3.2.8 beta (01.12.21) - Fix crash in opening shared media with another user. From 9f117cd680da9746bbd14f478693a0ebb2638623 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Tue, 7 Dec 2021 16:29:38 +0400 Subject: [PATCH 180/180] Version 3.3: Update submodules. --- Telegram/lib_base | 2 +- Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index e77f08a91..3b0decd74 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit e77f08a91a736b479e292092aca7a1ba56516076 +Subproject commit 3b0decd74b8aa63756e1e51b1d9ad32e8737e3f2 diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 5cf6981bb..e3c4d4991 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 5cf6981bbbc7098fab90d7a421e4599ec0013115 +Subproject commit e3c4d49912277379ed9ba6f300a5155b39337cb1