From cb5fdac0da0cb063fe3978a4220eca16a328a17d Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 9 Apr 2024 23:41:01 +0400 Subject: [PATCH 001/175] Replace mozjpeg with jpegli on Linux --- Telegram/build/docker/centos_env/Dockerfile | 31 +++++-------- cmake | 2 +- snap/snapcraft.yaml | 51 ++++++--------------- 3 files changed, 26 insertions(+), 58 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 964684ab8..16ae4b5d4 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -51,7 +51,7 @@ FROM builder AS patches RUN git init patches \ && cd patches \ && git remote add origin {{ GIT }}/desktop-app/patches.git \ - && git fetch --depth=1 origin b8e22ca1511513024b22b5381153163ad3406a8c \ + && git fetch --depth=1 origin f81dfc6f7061d7f70c7e952cee9c1c2689f937a2 \ && git reset --hard FETCH_HEAD \ && rm -rf .git @@ -132,19 +132,6 @@ RUN git clone -b 1.0.7 --depth=1 {{ GIT }}/google/highway.git \ && cd .. \ && rm -rf highway -FROM builder AS mozjpeg -RUN git clone -b v4.1.4 --depth=1 {{ GIT }}/mozilla/mozjpeg.git \ - && cd mozjpeg \ - && cmake -GNinja -B build . \ - -DCMAKE_BUILD_TYPE=None \ - -DCMAKE_INSTALL_PREFIX=/usr/local \ - -DWITH_JPEG8=ON \ - -DPNG_SUPPORTED=OFF \ - && cmake --build build --parallel \ - && DESTDIR="{{ LibrariesPath }}/mozjpeg-cache" cmake --install build \ - && cd .. \ - && rm -rf mozjpeg - FROM builder AS opus RUN git clone -b v1.4 --depth=1 {{ GIT }}/xiph/opus.git \ && cd opus \ @@ -258,20 +245,22 @@ RUN git clone -b v1.17.6 --depth=1 {{ GIT }}/strukturag/libheif.git \ && cd .. \ && rm -rf libheif -FROM builder AS libjxl +FROM patches AS libjxl COPY --link --from=lcms2 {{ LibrariesPath }}/lcms2-cache / COPY --link --from=brotli {{ LibrariesPath }}/brotli-cache / COPY --link --from=highway {{ LibrariesPath }}/highway-cache / RUN git clone -b v0.10.2 --depth=1 {{ GIT }}/libjxl/libjxl.git \ && cd libjxl \ + && git apply ../patches/libjxl.patch \ + && git submodule update --init --recursive --depth=1 third_party/libjpeg-turbo \ && cmake -GNinja -B build . \ -DCMAKE_BUILD_TYPE=None \ -DBUILD_SHARED_LIBS=OFF \ -DBUILD_TESTING=OFF \ -DJPEGXL_ENABLE_DEVTOOLS=OFF \ -DJPEGXL_ENABLE_TOOLS=OFF \ - -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF \ + -DJPEGXL_INSTALL_JPEGLI_LIBJPEG=ON \ -DJPEGXL_ENABLE_DOXYGEN=OFF \ -DJPEGXL_ENABLE_MANPAGES=OFF \ -DJPEGXL_ENABLE_BENCHMARK=OFF \ @@ -281,7 +270,10 @@ RUN git clone -b v0.10.2 --depth=1 {{ GIT }}/libjxl/libjxl.git \ -DJPEGXL_ENABLE_OPENEXR=OFF \ -DJPEGXL_ENABLE_SKCMS=OFF \ && cmake --build build --parallel \ - && DESTDIR="{{ LibrariesPath }}/libjxl-cache" cmake --install build \ + && export DESTDIR="{{ LibrariesPath }}/libjxl-cache" \ + && cmake --install build \ + && cp build/lib/libjpegli-static.a $DESTDIR/usr/local/lib64/libjpeg.a \ + && ar rcs $DESTDIR/usr/local/lib64/libjpeg.a build/lib/CMakeFiles/jpegli-libjpeg-obj.dir/jpegli/libjpeg_wrapper.cc.o \ && cd .. \ && rm -rf libjxl @@ -704,7 +696,7 @@ RUN git clone -b 1.78.1 --depth=1 {{ GIT }}/GNOME/gobject-introspection.git \ FROM patches AS qt COPY --link --from=zlib {{ LibrariesPath }}/zlib-cache / COPY --link --from=lcms2 {{ LibrariesPath }}/lcms2-cache / -COPY --link --from=mozjpeg {{ LibrariesPath }}/mozjpeg-cache / +COPY --link --from=libjxl {{ LibrariesPath }}/libjxl-cache / COPY --link --from=xcb {{ LibrariesPath }}/xcb-cache / COPY --link --from=xcb-wm {{ LibrariesPath }}/xcb-wm-cache / COPY --link --from=xcb-util {{ LibrariesPath }}/xcb-util-cache / @@ -757,9 +749,9 @@ RUN git clone -b v2023.06.01 --depth=1 https://chromium.googlesource.com/breakpa && rm -rf breakpad FROM builder AS webrtc -COPY --link --from=mozjpeg {{ LibrariesPath }}/mozjpeg-cache / COPY --link --from=opus {{ LibrariesPath }}/opus-cache / COPY --link --from=libvpx {{ LibrariesPath }}/libvpx-cache / +COPY --link --from=libjxl {{ LibrariesPath }}/libjxl-cache / COPY --link --from=ffmpeg {{ LibrariesPath }}/ffmpeg-cache / COPY --link --from=openssl {{ LibrariesPath }}/openssl-cache / COPY --link --from=libXtst {{ LibrariesPath }}/libXtst-cache / @@ -805,7 +797,6 @@ COPY --link --from=protobuf {{ LibrariesPath }}/protobuf-cache / COPY --link --from=lcms2 {{ LibrariesPath }}/lcms2-cache / COPY --link --from=brotli {{ LibrariesPath }}/brotli-cache / COPY --link --from=highway {{ LibrariesPath }}/highway-cache / -COPY --link --from=mozjpeg {{ LibrariesPath }}/mozjpeg-cache / COPY --link --from=opus {{ LibrariesPath }}/opus-cache / COPY --link --from=dav1d {{ LibrariesPath }}/dav1d-cache / COPY --link --from=libde265 {{ LibrariesPath }}/libde265-cache / diff --git a/cmake b/cmake index 4ec493812..621b01ec9 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 4ec493812d25de1eccfedccc49a23c15d032b118 +Subproject commit 621b01ec9bd7f953223147daf59ab4c850cb3c40 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 8a122ab95..af58384fb 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -131,6 +131,7 @@ parts: cmake-parameters: - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_INSTALL_PREFIX=/usr + - -DCMAKE_PREFIX_PATH=$CRAFT_STAGE/usr - -DTDESKTOP_API_ID=611335 - -DTDESKTOP_API_HASH=d524b414d21f4d37f08684c1df41ac9c - -DDESKTOP_APP_USE_PACKAGED_LAZY=ON @@ -153,12 +154,9 @@ parts: override-build: | craftctl default rm -rf "$CRAFT_PART_INSTALL/usr/share/icons" - stage: - - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libjpeg.so.8.2.2 after: - ffmpeg - libjxl - - mozjpeg - openal - protobuf - qt @@ -168,7 +166,7 @@ parts: patches: source: https://github.com/desktop-app/patches.git source-depth: 1 - source-commit: b8e22ca1511513024b22b5381153163ad3406a8c + source-commit: f81dfc6f7061d7f70c7e952cee9c1c2689f937a2 plugin: dump override-pull: | craftctl default @@ -209,8 +207,6 @@ parts: fi done GTK_PATH=$PWD/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gtk-3.0 /usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libgtk-3-0/gtk-query-immodules-3.0 > usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/gtk-3.0/3.0.0/immodules/immodules.cache - stage: - - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libjpeg.so.8.2.2 after: - telegram @@ -238,10 +234,6 @@ parts: rmdir "$CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/blas" mv "$CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/lapack/"* "$CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR" rmdir "$CRAFT_PART_INSTALL/usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/lapack" - stage: - - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libjpeg.so.8.2.2 - after: - - mozjpeg libjxl: source: https://github.com/libjxl/libjxl.git @@ -257,7 +249,7 @@ parts: - -DBUILD_TESTING=OFF - -DJPEGXL_ENABLE_DEVTOOLS=OFF - -DJPEGXL_ENABLE_TOOLS=OFF - - -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF + - -DJPEGXL_INSTALL_JPEGLI_LIBJPEG=ON - -DJPEGXL_ENABLE_DOXYGEN=OFF - -DJPEGXL_ENABLE_MANPAGES=OFF - -DJPEGXL_ENABLE_BENCHMARK=OFF @@ -266,6 +258,9 @@ parts: - -DJPEGXL_ENABLE_SJPEG=OFF - -DJPEGXL_ENABLE_OPENEXR=OFF - -DJPEGXL_ENABLE_SKCMS=OFF + override-pull: | + craftctl default + git apply $CRAFT_STAGE/patches/libjxl.patch stage: - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libbrotli* prime: @@ -275,27 +270,8 @@ parts: - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/*.a - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/*.so - -./usr/share - - mozjpeg: - source: https://github.com/mozilla/mozjpeg.git - source-depth: 1 - source-tag: v4.1.4 - plugin: cmake - build-environment: - - LDFLAGS: ${LDFLAGS:+$LDFLAGS} -s - cmake-generator: Ninja - cmake-parameters: - - -DCMAKE_BUILD_TYPE=Release - - -DCMAKE_INSTALL_PREFIX=/usr - - -DENABLE_STATIC=OFF - - -DWITH_JPEG8=ON - - -DPNG_SUPPORTED=OFF - prime: - - -./usr/bin - - -./usr/include - - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/pkgconfig - - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/*.so - - -./usr/share + after: + - patches openal: source: https://github.com/kcat/openal-soft.git @@ -458,12 +434,12 @@ parts: -confirm-license \ -openssl-linked \ -nomake examples \ - -nomake tests + -nomake tests \ + -- \ + -DCMAKE_PREFIX_PATH=$CRAFT_STAGE/usr cmake --build . -j$CRAFT_PARALLEL_BUILD_COUNT DESTDIR="$CRAFT_PART_INSTALL" cmake --install . - stage: - - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libjpeg.so.8.2.2 prime: - -./usr/bin - -./usr/doc @@ -479,7 +455,7 @@ parts: - -./usr/mkspecs - -./usr/modules after: - - mozjpeg + - libjxl - patches rnnoise: @@ -559,10 +535,11 @@ parts: cmake-parameters: - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_INSTALL_PREFIX=/usr + - -DCMAKE_PREFIX_PATH=$CRAFT_STAGE/usr prime: - -./usr/include - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/cmake - -./usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/*.a after: - ffmpeg - - mozjpeg + - libjxl From 8d7845daa105b24de441ea695784ce983b1663b5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 18 Apr 2024 06:22:29 +0400 Subject: [PATCH 002/175] Use --shallow-submodules in Docker --- Telegram/build/docker/centos_env/Dockerfile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 16ae4b5d4..2333305d7 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -77,7 +77,7 @@ RUN git clone -b v5.4.4 --depth=1 https://git.tukaani.org/xz.git \ && rm -rf xz FROM builder AS protobuf -RUN git clone -b v21.9 --depth=1 --recursive {{ GIT }}/protocolbuffers/protobuf.git \ +RUN git clone -b v21.9 --depth=1 --recursive --shallow-submodules {{ GIT }}/protocolbuffers/protobuf.git \ && cd protobuf \ && git init third_party/abseil-cpp \ && cd third_party/abseil-cpp \ @@ -310,7 +310,7 @@ RUN git clone -b libxcb-1.16 --depth=1 {{ GIT_FREEDESKTOP }}/libxcb.git \ && rm -rf libxcb FROM builder AS xcb-wm -RUN git clone -b xcb-util-wm-0.4.2 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/libxcb-wm.git \ +RUN git clone -b xcb-util-wm-0.4.2 --depth=1 --recursive --shallow-submodules {{ GIT_FREEDESKTOP }}/libxcb-wm.git \ && cd libxcb-wm \ && ./autogen.sh --enable-static \ && make -j$(nproc) \ @@ -319,7 +319,7 @@ RUN git clone -b xcb-util-wm-0.4.2 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/l && rm -rf libxcb-wm FROM builder AS xcb-util -RUN git clone -b xcb-util-0.4.1 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/libxcb-util.git \ +RUN git clone -b xcb-util-0.4.1 --depth=1 --recursive --shallow-submodules {{ GIT_FREEDESKTOP }}/libxcb-util.git \ && cd libxcb-util \ && ./autogen.sh --enable-static \ && make -j$(nproc) \ @@ -330,7 +330,7 @@ RUN git clone -b xcb-util-0.4.1 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/libx FROM builder AS xcb-image COPY --link --from=xcb-util {{ LibrariesPath }}/xcb-util-cache / -RUN git clone -b xcb-util-image-0.4.1 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/libxcb-image.git \ +RUN git clone -b xcb-util-image-0.4.1 --depth=1 --recursive --shallow-submodules {{ GIT_FREEDESKTOP }}/libxcb-image.git \ && cd libxcb-image \ && ./autogen.sh --enable-static \ && make -j$(nproc) \ @@ -339,7 +339,7 @@ RUN git clone -b xcb-util-image-0.4.1 --depth=1 --recursive {{ GIT_FREEDESKTOP } && rm -rf libxcb-image FROM builder AS xcb-keysyms -RUN git clone -b xcb-util-keysyms-0.4.1 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/libxcb-keysyms.git \ +RUN git clone -b xcb-util-keysyms-0.4.1 --depth=1 --recursive --shallow-submodules {{ GIT_FREEDESKTOP }}/libxcb-keysyms.git \ && cd libxcb-keysyms \ && ./autogen.sh --enable-static \ && make -j$(nproc) \ @@ -348,7 +348,7 @@ RUN git clone -b xcb-util-keysyms-0.4.1 --depth=1 --recursive {{ GIT_FREEDESKTOP && rm -rf libxcb-keysyms FROM builder AS xcb-render-util -RUN git clone -b xcb-util-renderutil-0.3.10 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/libxcb-render-util.git \ +RUN git clone -b xcb-util-renderutil-0.3.10 --depth=1 --recursive --shallow-submodules {{ GIT_FREEDESKTOP }}/libxcb-render-util.git \ && cd libxcb-render-util \ && ./autogen.sh --enable-static \ && make -j$(nproc) \ @@ -361,7 +361,7 @@ COPY --link --from=xcb-util {{ LibrariesPath }}/xcb-util-cache / COPY --link --from=xcb-image {{ LibrariesPath }}/xcb-image-cache / COPY --link --from=xcb-render-util {{ LibrariesPath }}/xcb-render-util-cache / -RUN git clone -b xcb-util-cursor-0.1.4 --depth=1 --recursive {{ GIT_FREEDESKTOP }}/libxcb-cursor.git \ +RUN git clone -b xcb-util-cursor-0.1.4 --depth=1 --recursive --shallow-submodules {{ GIT_FREEDESKTOP }}/libxcb-cursor.git \ && cd libxcb-cursor \ && ./autogen.sh --enable-static \ && make -j$(nproc) \ From 3510ca7184e791039a5c7807f4f4537756f7b050 Mon Sep 17 00:00:00 2001 From: Kolya <142352140+agl-1984@users.noreply.github.com> Date: Thu, 18 Apr 2024 00:31:04 +0200 Subject: [PATCH 003/175] Don't use/link libsharpyuv in libavif --- Telegram/build/prepare/prepare.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index b2e59ef31..55a6cd795 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -812,7 +812,8 @@ mac: -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\ -D BUILD_SHARED_LIBS=OFF \\ -D AVIF_ENABLE_WERROR=OFF \\ - -D AVIF_CODEC_DAV1D=ON + -D AVIF_CODEC_DAV1D=ON \\ + -D CMAKE_DISABLE_FIND_PACKAGE_libsharpyuv=ON cmake --build . --config MinSizeRel $MAKE_THREADS_CNT cmake --install . --config MinSizeRel """) From 01bfa467295684d6ca0943024ca848bddf7a1484 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 18 Apr 2024 04:02:40 +0300 Subject: [PATCH 004/175] Fixed display of default box for message schedule in correspond window. --- Telegram/SourceFiles/boxes/sticker_set_box.cpp | 2 +- Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp | 2 +- Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp | 2 +- Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp | 2 +- .../view/controls/history_view_compose_controls.cpp | 2 +- Telegram/SourceFiles/inline_bots/inline_results_inner.cpp | 2 +- Telegram/SourceFiles/menu/menu_send.cpp | 7 +++---- Telegram/SourceFiles/menu/menu_send.h | 3 ++- Telegram/SourceFiles/window/window_peer_menu.cpp | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 88163356c..ac4555120 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -1032,7 +1032,7 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) { _menu.get(), type, SendMenu::DefaultSilentCallback(sendSelected), - SendMenu::DefaultScheduleCallback(this, type, sendSelected), + SendMenu::DefaultScheduleCallback(_show, type, sendSelected), SendMenu::DefaultWhenOnlineCallback(sendSelected)); const auto show = _show; diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 9bc6e88f2..20f32f6e8 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -1379,7 +1379,7 @@ void FieldAutocomplete::Inner::contextMenuEvent(QContextMenuEvent *e) { _menu, type, SendMenu::DefaultSilentCallback(send), - SendMenu::DefaultScheduleCallback(this, type, send), + SendMenu::DefaultScheduleCallback(_show, type, send), SendMenu::DefaultWhenOnlineCallback(send)); if (!_menu->empty()) { diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 13cfda86d..72794de39 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -394,7 +394,7 @@ base::unique_qptr GifsListWidget::fillContextMenu( menu, type, SendMenu::DefaultSilentCallback(send), - SendMenu::DefaultScheduleCallback(this, type, send), + SendMenu::DefaultScheduleCallback(_show, type, send), SendMenu::DefaultWhenOnlineCallback(send), icons); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index a6a52f4be..42ea6b934 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -1654,7 +1654,7 @@ base::unique_qptr StickersListWidget::fillContextMenu( menu, type, SendMenu::DefaultSilentCallback(send), - SendMenu::DefaultScheduleCallback(this, type, send), + SendMenu::DefaultScheduleCallback(_show, type, send), SendMenu::DefaultWhenOnlineCallback(send), icons); 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 d47ee6ec1..dff29122c 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -2183,7 +2183,7 @@ void ComposeControls::initSendButton() { _send.get(), [=] { return sendButtonMenuType(); }, SendMenu::DefaultSilentCallback(send), - SendMenu::DefaultScheduleCallback(_wrap.get(), sendMenuType(), send), + SendMenu::DefaultScheduleCallback(_show, sendMenuType(), send), SendMenu::DefaultWhenOnlineCallback(send)); } diff --git a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp index 0103789ec..5e45b3d4a 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp @@ -344,7 +344,7 @@ void Inner::contextMenuEvent(QContextMenuEvent *e) { _menu, type, SendMenu::DefaultSilentCallback(send), - SendMenu::DefaultScheduleCallback(this, type, send), + SendMenu::DefaultScheduleCallback(_controller->uiShow(), type, send), SendMenu::DefaultWhenOnlineCallback(send)); const auto item = _mosaic.itemAt(_selected); diff --git a/Telegram/SourceFiles/menu/menu_send.cpp b/Telegram/SourceFiles/menu/menu_send.cpp index 02ab8ed4b..a31fcf103 100644 --- a/Telegram/SourceFiles/menu/menu_send.cpp +++ b/Telegram/SourceFiles/menu/menu_send.cpp @@ -34,12 +34,11 @@ Fn DefaultSilentCallback(Fn send) { } Fn DefaultScheduleCallback( - not_null parent, + std::shared_ptr show, Type type, Fn send) { - const auto weak = Ui::MakeWeak(parent); - return [=] { - Ui::show( + return [=, weak = Ui::MakeWeak(show->toastParent())] { + show->showBox( HistoryView::PrepareScheduleBox( weak, type, diff --git a/Telegram/SourceFiles/menu/menu_send.h b/Telegram/SourceFiles/menu/menu_send.h index 79edc3840..03b7f7ec5 100644 --- a/Telegram/SourceFiles/menu/menu_send.h +++ b/Telegram/SourceFiles/menu/menu_send.h @@ -18,6 +18,7 @@ struct SendOptions; namespace Ui { class PopupMenu; class RpWidget; +class Show; } // namespace Ui namespace Data { @@ -41,7 +42,7 @@ enum class FillMenuResult { Fn DefaultSilentCallback(Fn send); Fn DefaultScheduleCallback( - not_null parent, + std::shared_ptr show, Type type, Fn send); Fn DefaultWhenOnlineCallback(Fn send); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 510902a15..2d3e2a663 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -2046,7 +2046,7 @@ QPointer ShowForwardMessagesBox( state->menu.get(), type, SendMenu::DefaultSilentCallback(submit), - SendMenu::DefaultScheduleCallback(state->box, type, submit), + SendMenu::DefaultScheduleCallback(show, type, submit), SendMenu::DefaultWhenOnlineCallback(submit)); const auto success = (result == SendMenu::FillMenuResult::Success); if (showForwardOptions || success) { From 4310c4978e8b68163b8b0d184a6cdd8011f3978a Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Thu, 18 Apr 2024 22:21:13 +0400 Subject: [PATCH 005/175] Fix macOS packaged action --- .github/workflows/mac_packaged.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mac_packaged.yml b/.github/workflows/mac_packaged.yml index 8ed4ffca1..d04b284de 100644 --- a/.github/workflows/mac_packaged.yml +++ b/.github/workflows/mac_packaged.yml @@ -49,7 +49,7 @@ jobs: env: GIT: "https://github.com" - OPENALDIR: "/usr/local/opt/openal-soft" + CMAKE_PREFIX_PATH: "/usr/local/opt/ffmpeg@6:/usr/local/opt/openal-soft" UPLOAD_ARTIFACT: "true" ONLY_CACHE: "false" MANUAL_CACHING: "1" @@ -69,7 +69,7 @@ jobs: run: | brew update brew upgrade || true - brew install autoconf automake boost cmake ffmpeg openal-soft openssl opus ninja pkg-config python qt yasm xz + brew install autoconf automake boost cmake ffmpeg@6 openal-soft openssl opus ninja pkg-config python qt yasm xz sudo xcode-select -s /Applications/Xcode.app/Contents/Developer xcodebuild -version > CACHE_KEY.txt From 3d994b58a08c1b26dd845dbdba480a65dcd16b82 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 21 Apr 2024 01:20:59 +0400 Subject: [PATCH 006/175] ShowXDPOpenWithDialog -> UnsafeShowOpenWith --- Telegram/CMakeLists.txt | 2 - .../platform/linux/file_utilities_linux.cpp | 119 ++++++++++++++++ .../platform/linux/file_utilities_linux.h | 6 - .../linux/linux_xdp_open_with_dialog.cpp | 129 ------------------ .../linux/linux_xdp_open_with_dialog.h | 18 --- 5 files changed, 119 insertions(+), 155 deletions(-) delete mode 100644 Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp delete mode 100644 Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index c99cd34b5..d0ea8561d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1202,8 +1202,6 @@ PRIVATE platform/linux/linux_wayland_integration_dummy.cpp platform/linux/linux_wayland_integration.cpp platform/linux/linux_wayland_integration.h - platform/linux/linux_xdp_open_with_dialog.cpp - platform/linux/linux_xdp_open_with_dialog.h platform/linux/file_utilities_linux.cpp platform/linux/file_utilities_linux.h platform/linux/launcher_linux.cpp diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index 2f9c0d9f9..d79e4778e 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -6,3 +6,122 @@ For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "platform/linux/file_utilities_linux.h" + +#include "base/platform/base_platform_info.h" +#include "base/platform/linux/base_linux_xdp_utilities.h" +#include "base/platform/linux/base_linux_xdg_activation_token.h" +#include "base/random.h" + +#include +#include +#include + +namespace Platform { +namespace File { +namespace { + +using namespace gi::repository; +using base::Platform::XdgActivationToken; + +} // namespace + +bool UnsafeShowOpenWith(const QString &filepath) { + auto proxy = XdpOpenURI::OpenURIProxy::new_for_bus_sync( + Gio::BusType::SESSION_, + Gio::DBusProxyFlags::NONE_, + base::Platform::XDP::kService, + base::Platform::XDP::kObjectPath, + nullptr); + + if (!proxy) { + return false; + } + + auto interface = XdpOpenURI::OpenURI(proxy); + if (interface.get_version() < 3) { + return false; + } + + const auto fd = open( + QFile::encodeName(filepath).constData(), + O_RDONLY); + + if (fd == -1) { + return false; + } + + const auto fdGuard = gsl::finally([&] { close(fd); }); + + const auto handleToken = "tdesktop" + + std::to_string(base::RandomValue()); + + std::string uniqueName = proxy.get_connection().get_unique_name(); + uniqueName.erase(0, 1); + uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); + + auto request = XdpRequest::Request( + XdpRequest::RequestProxy::new_sync( + proxy.get_connection(), + Gio::DBusProxyFlags::NONE_, + base::Platform::XDP::kService, + base::Platform::XDP::kObjectPath + + std::string("/request/") + + uniqueName + + '/' + + handleToken, + nullptr, + nullptr)); + + if (!request) { + return false; + } + + auto loop = GLib::MainLoop::new_(); + + const auto signalId = request.signal_response().connect([=]( + XdpRequest::Request, + guint, + GLib::Variant) mutable { + loop.quit(); + }); + + const auto signalGuard = gsl::finally([&] { + request.disconnect(signalId); + }); + + auto result = interface.call_open_file_sync( + base::Platform::XDP::ParentWindowID(), + GLib::Variant::new_handle(0), + GLib::Variant::new_array({ + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("handle_token"), + GLib::Variant::new_variant( + GLib::Variant::new_string(handleToken))), + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("activation_token"), + GLib::Variant::new_variant( + GLib::Variant::new_string( + XdgActivationToken().toStdString()))), + GLib::Variant::new_dict_entry( + GLib::Variant::new_string("ask"), + GLib::Variant::new_variant( + GLib::Variant::new_boolean(true))), + }), + Gio::UnixFDList::new_from_array((std::array{ fd }).data(), 1), + nullptr); + + if (!result) { + return false; + } + + QWidget window; + window.setAttribute(Qt::WA_DontShowOnScreen); + window.setWindowModality(Qt::ApplicationModal); + window.show(); + loop.run(); + + return true; +} + +} // namespace File +} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h index baced1178..2054d3f32 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h @@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "platform/platform_file_utilities.h" -#include "platform/linux/linux_xdp_open_with_dialog.h" - namespace Platform { namespace File { @@ -30,10 +28,6 @@ inline bool UnsafeShowOpenWithDropdown(const QString &filepath) { return false; } -inline bool UnsafeShowOpenWith(const QString &filepath) { - return internal::ShowXDPOpenWithDialog(filepath); -} - inline void UnsafeLaunch(const QString &filepath) { return ::File::internal::UnsafeLaunchDefault(filepath); } diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp deleted file mode 100644 index 9e409b900..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.cpp +++ /dev/null @@ -1,129 +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 "platform/linux/linux_xdp_open_with_dialog.h" - -#include "base/platform/base_platform_info.h" -#include "base/platform/linux/base_linux_xdp_utilities.h" -#include "base/platform/linux/base_linux_xdg_activation_token.h" -#include "base/random.h" - -#include -#include -#include - -namespace Platform { -namespace File { -namespace internal { -namespace { - -using namespace gi::repository; -using base::Platform::XdgActivationToken; - -} // namespace - -bool ShowXDPOpenWithDialog(const QString &filepath) { - auto proxy = XdpOpenURI::OpenURIProxy::new_for_bus_sync( - Gio::BusType::SESSION_, - Gio::DBusProxyFlags::NONE_, - base::Platform::XDP::kService, - base::Platform::XDP::kObjectPath, - nullptr); - - if (!proxy) { - return false; - } - - auto interface = XdpOpenURI::OpenURI(proxy); - if (interface.get_version() < 3) { - return false; - } - - const auto fd = open( - QFile::encodeName(filepath).constData(), - O_RDONLY); - - if (fd == -1) { - return false; - } - - const auto fdGuard = gsl::finally([&] { close(fd); }); - - const auto handleToken = "tdesktop" - + std::to_string(base::RandomValue()); - - std::string uniqueName = proxy.get_connection().get_unique_name(); - uniqueName.erase(0, 1); - uniqueName.replace(uniqueName.find('.'), 1, 1, '_'); - - auto request = XdpRequest::Request( - XdpRequest::RequestProxy::new_sync( - proxy.get_connection(), - Gio::DBusProxyFlags::NONE_, - base::Platform::XDP::kService, - base::Platform::XDP::kObjectPath - + std::string("/request/") - + uniqueName - + '/' - + handleToken, - nullptr, - nullptr)); - - if (!request) { - return false; - } - - auto loop = GLib::MainLoop::new_(); - - const auto signalId = request.signal_response().connect([=]( - XdpRequest::Request, - guint, - GLib::Variant) mutable { - loop.quit(); - }); - - const auto signalGuard = gsl::finally([&] { - request.disconnect(signalId); - }); - - auto result = interface.call_open_file_sync( - base::Platform::XDP::ParentWindowID(), - GLib::Variant::new_handle(0), - GLib::Variant::new_array({ - GLib::Variant::new_dict_entry( - GLib::Variant::new_string("handle_token"), - GLib::Variant::new_variant( - GLib::Variant::new_string(handleToken))), - GLib::Variant::new_dict_entry( - GLib::Variant::new_string("activation_token"), - GLib::Variant::new_variant( - GLib::Variant::new_string( - XdgActivationToken().toStdString()))), - GLib::Variant::new_dict_entry( - GLib::Variant::new_string("ask"), - GLib::Variant::new_variant( - GLib::Variant::new_boolean(true))), - }), - Gio::UnixFDList::new_from_array((std::array{ fd }).data(), 1), - nullptr); - - if (!result) { - return false; - } - - QWidget window; - window.setAttribute(Qt::WA_DontShowOnScreen); - window.setWindowModality(Qt::ApplicationModal); - window.show(); - loop.run(); - - return true; -} - -} // namespace internal -} // namespace File -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.h b/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.h deleted file mode 100644 index a848baaf5..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_xdp_open_with_dialog.h +++ /dev/null @@ -1,18 +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 Platform { -namespace File { -namespace internal { - -bool ShowXDPOpenWithDialog(const QString &filepath); - -} // namespace internal -} // namespace File -} // namespace Platform From 9046b2cafbd54331a84890bf83a0d3de3bc86bf3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 21 Apr 2024 00:54:48 +0400 Subject: [PATCH 007/175] Stop using the plasma-shell protocol Looks like this isn't really correct in combination with xdg-shell --- .gitmodules | 3 - Telegram/CMakeLists.txt | 26 --- .../linux/linux_wayland_integration.cpp | 150 ------------------ .../linux/linux_wayland_integration.h | 29 ---- .../linux/linux_wayland_integration_dummy.cpp | 37 ----- .../platform/linux/main_window_linux.cpp | 7 - .../platform/linux/specific_linux.cpp | 6 - Telegram/ThirdParty/plasma-wayland-protocols | 1 - 8 files changed, 259 deletions(-) delete mode 100644 Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp delete mode 100644 Telegram/SourceFiles/platform/linux/linux_wayland_integration.h delete mode 100644 Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp delete mode 160000 Telegram/ThirdParty/plasma-wayland-protocols diff --git a/.gitmodules b/.gitmodules index f611a0acc..dfc92dbf1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -82,9 +82,6 @@ [submodule "Telegram/ThirdParty/dispatch"] path = Telegram/ThirdParty/dispatch url = https://github.com/apple/swift-corelibs-libdispatch -[submodule "Telegram/ThirdParty/plasma-wayland-protocols"] - path = Telegram/ThirdParty/plasma-wayland-protocols - url = https://github.com/KDE/plasma-wayland-protocols.git [submodule "Telegram/ThirdParty/wayland-protocols"] path = Telegram/ThirdParty/wayland-protocols url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d0ea8561d..baf77c58c 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1199,9 +1199,6 @@ PRIVATE payments/payments_checkout_process.h payments/payments_form.cpp payments/payments_form.h - platform/linux/linux_wayland_integration_dummy.cpp - platform/linux/linux_wayland_integration.cpp - platform/linux/linux_wayland_integration.h platform/linux/file_utilities_linux.cpp platform/linux/file_utilities_linux.h platform/linux/launcher_linux.cpp @@ -1554,16 +1551,6 @@ if (NOT build_winstore) ) endif() -if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) - remove_target_sources(Telegram ${src_loc} - platform/linux/linux_wayland_integration.cpp - ) -else() - remove_target_sources(Telegram ${src_loc} - platform/linux/linux_wayland_integration_dummy.cpp - ) -endif() - if (DESKTOP_APP_USE_PACKAGED) remove_target_sources(Telegram ${src_loc} platform/mac/mac_iconv_helper.c @@ -1701,19 +1688,6 @@ else() desktop-app::external_xcb ) endif() - - if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) - qt_generate_wayland_protocol_client_sources(Telegram - FILES - ${third_party_loc}/wayland/protocol/wayland.xml - ${third_party_loc}/plasma-wayland-protocols/src/protocols/plasma-shell.xml - ) - - target_link_libraries(Telegram - PRIVATE - desktop-app::external_wayland_client - ) - endif() endif() if (build_macstore) diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp deleted file mode 100644 index c1485dd8c..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.cpp +++ /dev/null @@ -1,150 +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 "platform/linux/linux_wayland_integration.h" - -#include "base/platform/linux/base_linux_wayland_utilities.h" -#include "base/qt_signal_producer.h" -#include "base/flat_map.h" - -#include -#include -#include -#include - -#include -#include - -using QWlApp = QNativeInterface::QWaylandApplication; -using namespace QNativeInterface::Private; -using namespace base::Platform::Wayland; - -namespace Platform { -namespace internal { -namespace { - -class PlasmaShell : public Global { -public: - using Global::Global; - - using Surface = AutoDestroyer; - base::flat_map surfaces; -}; - -} // namespace - -struct WaylandIntegration::Private - : public AutoDestroyer { - Private(not_null native) - : AutoDestroyer(wl_display_get_registry(native->display())) - , display(native->display()) { - wl_display_roundtrip(display); - } - - QtWayland::org_kde_plasma_surface plasmaSurface(QWindow *window); - - const not_null display; - std::optional plasmaShell; - -protected: - void registry_global( - uint32_t name, - const QString &interface, - uint32_t version) override { - if (interface == qstr("org_kde_plasma_shell")) { - plasmaShell.emplace(object(), name, version); - } - } - - void registry_global_remove(uint32_t name) override { - if (plasmaShell && name == plasmaShell->id()) { - plasmaShell = std::nullopt; - } - } -}; - -QtWayland::org_kde_plasma_surface WaylandIntegration::Private::plasmaSurface( - QWindow *window) { - if (!plasmaShell) { - return {}; - } - - const auto native = window->nativeInterface(); - if (!native) { - return {}; - } - - const auto surface = native->surface(); - if (!surface) { - return {}; - } - - const auto it = plasmaShell->surfaces.find(surface); - if (it != plasmaShell->surfaces.cend()) { - return it->second; - } - - const auto plasmaSurface = plasmaShell->get_surface(surface); - if (!plasmaSurface) { - return {}; - } - - const auto result = plasmaShell->surfaces.emplace(surface, plasmaSurface); - - base::qt_signal_producer( - native, - &QWaylandWindow::surfaceDestroyed - ) | rpl::start_with_next([=] { - auto it = plasmaShell->surfaces.find(surface); - if (it != plasmaShell->surfaces.cend()) { - plasmaShell->surfaces.erase(it); - } - }, result.first->second.lifetime()); - - return result.first->second; -} - -WaylandIntegration::WaylandIntegration() -: _private(std::make_unique(qApp->nativeInterface())) { -} - -WaylandIntegration::~WaylandIntegration() = default; - -WaylandIntegration *WaylandIntegration::Instance() { - const auto native = qApp->nativeInterface(); - if (!native) return nullptr; - static std::optional instance; - if (instance && native->display() != instance->_private->display) { - instance.reset(); - } - if (!instance) { - instance.emplace(); - base::qt_signal_producer( - QGuiApplication::platformNativeInterface(), - &QObject::destroyed - ) | rpl::start_with_next([] { - instance = std::nullopt; - }, instance->_private->lifetime()); - } - return &*instance; -} - -bool WaylandIntegration::skipTaskbarSupported() { - return _private->plasmaShell.has_value(); -} - -void WaylandIntegration::skipTaskbar(QWindow *window, bool skip) { - auto plasmaSurface = _private->plasmaSurface(window); - if (!plasmaSurface.isInitialized()) { - return; - } - - plasmaSurface.set_skip_taskbar(skip); -} - -} // namespace internal -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h b/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h deleted file mode 100644 index bb74be0b3..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration.h +++ /dev/null @@ -1,29 +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 Platform { -namespace internal { - -class WaylandIntegration { -public: - WaylandIntegration(); - ~WaylandIntegration(); - - [[nodiscard]] static WaylandIntegration *Instance(); - - [[nodiscard]] bool skipTaskbarSupported(); - void skipTaskbar(QWindow *window, bool skip); - -private: - struct Private; - const std::unique_ptr _private; -}; - -} // namespace internal -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp b/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp deleted file mode 100644 index b4151306d..000000000 --- a/Telegram/SourceFiles/platform/linux/linux_wayland_integration_dummy.cpp +++ /dev/null @@ -1,37 +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 "platform/linux/linux_wayland_integration.h" - -#include "base/platform/base_platform_info.h" - -namespace Platform { -namespace internal { - -struct WaylandIntegration::Private { -}; - -WaylandIntegration::WaylandIntegration() { -} - -WaylandIntegration::~WaylandIntegration() = default; - -WaylandIntegration *WaylandIntegration::Instance() { - if (!IsWayland()) return nullptr; - static WaylandIntegration instance; - return &instance; -} - -bool WaylandIntegration::skipTaskbarSupported() { - return false; -} - -void WaylandIntegration::skipTaskbar(QWindow *window, bool skip) { -} - -} // namespace internal -} // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index c8218ca7d..ba73e3b3a 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_window.h" #include "platform/linux/specific_linux.h" -#include "platform/linux/linux_wayland_integration.h" #include "history/history.h" #include "history/history_widget.h" #include "history/history_inner_widget.h" @@ -48,7 +47,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Platform { namespace { -using internal::WaylandIntegration; using WorkMode = Core::Settings::WorkMode; #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION @@ -102,11 +100,6 @@ void XCBSkipTaskbar(QWindow *window, bool skip) { #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION void SkipTaskbar(QWindow *window, bool skip) { - if (const auto integration = WaylandIntegration::Instance()) { - integration->skipTaskbar(window, skip); - return; - } - #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION if (IsX11()) { XCBSkipTaskbar(window, skip); diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 29b887941..48c189ca2 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/platform/linux/base_linux_dbus_utilities.h" #include "base/platform/linux/base_linux_xdp_utilities.h" #include "ui/platform/ui_platform_window_title.h" -#include "platform/linux/linux_wayland_integration.h" #include "lang/lang_keys.h" #include "mainwindow.h" #include "storage/localstorage.h" @@ -53,7 +52,6 @@ namespace { using namespace gi::repository; namespace GObject = gi::repository::GObject; using namespace Platform; -using Platform::internal::WaylandIntegration; void PortalAutostart(bool enabled, Fn done) { const auto executable = ExecutablePathForShortcuts(); @@ -563,10 +561,6 @@ bool TrayIconSupported() { } bool SkipTaskbarSupported() { - if (const auto integration = WaylandIntegration::Instance()) { - return integration->skipTaskbarSupported(); - } - #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION if (IsX11()) { return base::Platform::XCB::IsSupportedByWM( diff --git a/Telegram/ThirdParty/plasma-wayland-protocols b/Telegram/ThirdParty/plasma-wayland-protocols deleted file mode 160000 index 78fc6ee77..000000000 --- a/Telegram/ThirdParty/plasma-wayland-protocols +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 78fc6ee77334a147986f01c6d3c6e1b99af1a333 From 0ad18c81829c77da4ce115928db776d0d4723cb7 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 21 Apr 2024 01:02:30 +0400 Subject: [PATCH 008/175] Get rid of direct Wayland usage This should get rid of a very nasty vector of misbehaving and quite a lot of code which is boilerplate over Wayland protocols at the price of losing support of niche features Most of things served by xcb code on X11 is served by portal code on Wayland, hopefully this tendention will continue and we will be fine just with glib which provides D-Bus and other basic APIs --- .github/workflows/linux.yml | 1 - .gitmodules | 6 ------ Telegram/ThirdParty/wayland | 1 - Telegram/ThirdParty/wayland-protocols | 1 - Telegram/lib_base | 2 +- cmake | 2 +- 6 files changed, 2 insertions(+), 11 deletions(-) delete mode 160000 Telegram/ThirdParty/wayland delete mode 160000 Telegram/ThirdParty/wayland-protocols diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 4eaf2ec22..8020e46c5 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -49,7 +49,6 @@ jobs: defines: - "" - "DESKTOP_APP_DISABLE_X11_INTEGRATION" - - "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION" env: UPLOAD_ARTIFACT: "true" diff --git a/.gitmodules b/.gitmodules index dfc92dbf1..91f154826 100644 --- a/.gitmodules +++ b/.gitmodules @@ -82,9 +82,6 @@ [submodule "Telegram/ThirdParty/dispatch"] path = Telegram/ThirdParty/dispatch url = https://github.com/apple/swift-corelibs-libdispatch -[submodule "Telegram/ThirdParty/wayland-protocols"] - path = Telegram/ThirdParty/wayland-protocols - url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git [submodule "Telegram/ThirdParty/kimageformats"] path = Telegram/ThirdParty/kimageformats url = https://github.com/KDE/kimageformats.git @@ -94,9 +91,6 @@ [submodule "Telegram/ThirdParty/cld3"] path = Telegram/ThirdParty/cld3 url = https://github.com/google/cld3.git -[submodule "Telegram/ThirdParty/wayland"] - path = Telegram/ThirdParty/wayland - url = https://github.com/gitlab-freedesktop-mirrors/wayland.git [submodule "Telegram/ThirdParty/libprisma"] path = Telegram/ThirdParty/libprisma url = https://github.com/desktop-app/libprisma.git diff --git a/Telegram/ThirdParty/wayland b/Telegram/ThirdParty/wayland deleted file mode 160000 index b2649cb3e..000000000 --- a/Telegram/ThirdParty/wayland +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b2649cb3ee6bd70828a17e50beb16591e6066288 diff --git a/Telegram/ThirdParty/wayland-protocols b/Telegram/ThirdParty/wayland-protocols deleted file mode 160000 index 4624cfaaf..000000000 --- a/Telegram/ThirdParty/wayland-protocols +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4624cfaaf563cd7be5e2e2087c8de6d3a48ea867 diff --git a/Telegram/lib_base b/Telegram/lib_base index 3d1ea724a..d5a6bfced 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 3d1ea724a80c3e3a431a88a653d24f4f418eaea8 +Subproject commit d5a6bfced0b1fa35c4f601816313d02e6ecb7cde diff --git a/cmake b/cmake index 621b01ec9..a428df544 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 621b01ec9bd7f953223147daf59ab4c850cb3c40 +Subproject commit a428df5440e76a726abc30924766ac7da0cb381c From 8d2805f22619e1ba719583459d12afce0827d9c9 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 23 Apr 2024 09:39:28 +0400 Subject: [PATCH 009/175] Set font weight in crash reporter --- Telegram/SourceFiles/core/crash_report_window.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Telegram/SourceFiles/core/crash_report_window.cpp b/Telegram/SourceFiles/core/crash_report_window.cpp index 41bacf4ba..3c19013c9 100644 --- a/Telegram/SourceFiles/core/crash_report_window.cpp +++ b/Telegram/SourceFiles/core/crash_report_window.cpp @@ -100,6 +100,7 @@ PreLaunchWindow::~PreLaunchWindow() { PreLaunchLabel::PreLaunchLabel(QWidget *parent) : QLabel(parent) { QFont labelFont(font()); labelFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold)); + labelFont.setWeight(QFont::DemiBold); labelFont.setPixelSize(static_cast(parent)->basicSize()); setFont(labelFont); @@ -163,6 +164,7 @@ PreLaunchButton::PreLaunchButton(QWidget *parent, bool confirm) : QPushButton(pa QFont closeFont(font()); closeFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold)); + closeFont.setWeight(QFont::DemiBold); closeFont.setPixelSize(static_cast(parent)->basicSize()); setFont(closeFont); @@ -182,6 +184,7 @@ PreLaunchCheckbox::PreLaunchCheckbox(QWidget *parent) : QCheckBox(parent) { QFont closeFont(font()); closeFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold)); + closeFont.setWeight(QFont::DemiBold); closeFont.setPixelSize(static_cast(parent)->basicSize()); setFont(closeFont); From bae9af1076e25517ff657efbbf3aad9839c0f29f Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 23 Apr 2024 09:39:28 +0400 Subject: [PATCH 010/175] Switch crash reporter to system fonts This should prevent nested crashes when loading custom fonts --- Telegram/SourceFiles/core/crash_report_window.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Telegram/SourceFiles/core/crash_report_window.cpp b/Telegram/SourceFiles/core/crash_report_window.cpp index 3c19013c9..b73c12aed 100644 --- a/Telegram/SourceFiles/core/crash_report_window.cpp +++ b/Telegram/SourceFiles/core/crash_report_window.cpp @@ -32,8 +32,6 @@ constexpr auto kDefaultProxyPort = 80; PreLaunchWindow *PreLaunchWindowInstance = nullptr; PreLaunchWindow::PreLaunchWindow(QString title) { - style::internal::StartFonts(); - setWindowIcon(Window::CreateIcon()); setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); @@ -99,7 +97,6 @@ PreLaunchWindow::~PreLaunchWindow() { PreLaunchLabel::PreLaunchLabel(QWidget *parent) : QLabel(parent) { QFont labelFont(font()); - labelFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold)); labelFont.setWeight(QFont::DemiBold); labelFont.setPixelSize(static_cast(parent)->basicSize()); setFont(labelFont); @@ -119,7 +116,6 @@ void PreLaunchLabel::setText(const QString &text) { PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(parent) { QFont logFont(font()); - logFont.setFamily(style::internal::GetFontOverride()); logFont.setPixelSize(static_cast(parent)->basicSize()); setFont(logFont); @@ -140,7 +136,6 @@ PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(paren PreLaunchLog::PreLaunchLog(QWidget *parent) : QTextEdit(parent) { QFont logFont(font()); - logFont.setFamily(style::internal::GetFontOverride()); logFont.setPixelSize(static_cast(parent)->basicSize()); setFont(logFont); @@ -163,7 +158,6 @@ PreLaunchButton::PreLaunchButton(QWidget *parent, bool confirm) : QPushButton(pa setObjectName(confirm ? "confirm" : "cancel"); QFont closeFont(font()); - closeFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold)); closeFont.setWeight(QFont::DemiBold); closeFont.setPixelSize(static_cast(parent)->basicSize()); setFont(closeFont); @@ -183,7 +177,6 @@ PreLaunchCheckbox::PreLaunchCheckbox(QWidget *parent) : QCheckBox(parent) { setCheckState(Qt::Checked); QFont closeFont(font()); - closeFont.setFamily(style::internal::GetFontOverride(style::internal::FontSemibold)); closeFont.setWeight(QFont::DemiBold); closeFont.setPixelSize(static_cast(parent)->basicSize()); setFont(closeFont); From 18f5521be5e8c230b589eb4317a2d7d8f421f8f0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Apr 2024 19:06:22 +0400 Subject: [PATCH 011/175] Don't look for SDL when building on macOS. --- Telegram/build/prepare/prepare.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 55a6cd795..8a647d419 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -847,6 +847,7 @@ mac: -D CMAKE_OSX_DEPLOYMENT_TARGET:STRING=$MACOSX_DEPLOYMENT_TARGET \\ -D CMAKE_INSTALL_PREFIX:STRING=$USED_PREFIX \\ -D DISABLE_SSE=ON \\ + -D ENABLE_SDL=OFF \\ -D BUILD_SHARED_LIBS=OFF \\ -D ENABLE_DECODER=ON \\ -D ENABLE_ENCODER=OFF From dc438cff23ab33ed9e83320b8d9f1369071cbbdc Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Apr 2024 19:10:44 +0400 Subject: [PATCH 012/175] Always export file_name in json. --- Telegram/SourceFiles/export/output/export_output_json.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp index ed50aa5c8..67814e5b2 100644 --- a/Telegram/SourceFiles/export/output/export_output_json.cpp +++ b/Telegram/SourceFiles/export/output/export_output_json.cpp @@ -653,6 +653,7 @@ QByteArray SerializeMessage( pushTTL(); }, [&](const Document &data) { pushPath(data.file, "file"); + push("file_name", data.name); if (data.thumb.width > 0) { pushPath(data.thumb.file, "thumbnail"); } @@ -675,9 +676,7 @@ QByteArray SerializeMessage( push("performer", data.songPerformer); push("title", data.songTitle); } - if (!data.isSticker) { - push("mime_type", data.mime); - } + push("mime_type", data.mime); if (data.duration) { push("duration_seconds", data.duration); } From 2f03b9aa299f3ec899668c9c937c1691c48398fd Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 8 Apr 2024 11:50:26 +0400 Subject: [PATCH 013/175] Make dialogs filter onfocused by default. --- .../SourceFiles/dialogs/dialogs_widget.cpp | 149 ++++++++++-------- Telegram/SourceFiles/dialogs/dialogs_widget.h | 10 +- .../window/window_session_controller.cpp | 8 +- 3 files changed, 89 insertions(+), 78 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 873b15324..b2b1a8fe1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -214,8 +214,8 @@ Widget::Widget( st::dialogsMenuToggle), .under = object_ptr(_searchControls), }) -, _searchForNarrowFilters(_searchControls, st::dialogsSearchForNarrowFilters) -, _filter(_searchControls, st::dialogsFilter, tr::lng_dlg_filter()) +, _searchForNarrowLayout(_searchControls, st::dialogsSearchForNarrowFilters) +, _search(_searchControls, st::dialogsFilter, tr::lng_dlg_filter()) , _chooseFromUser( _searchControls, object_ptr(this, st::dialogsSearchFrom)) @@ -321,7 +321,7 @@ Widget::Widget( }, lifetime()); _inner->refreshHashtagsRequests( ) | rpl::start_with_next([=] { - filterCursorMoved(); + searchCursorMoved(); }, lifetime()); _inner->cancelSearchFromUserRequests( ) | rpl::start_with_next([=] { @@ -330,7 +330,7 @@ Widget::Widget( : _searchInChat.sublist() ? Key(session().data().history(session().user())) : _searchInChat, nullptr); - applyFilterUpdate(true); + applySearchUpdate(true); }, lifetime()); _inner->chosenRow( ) | rpl::start_with_next([=](const ChosenRow &row) { @@ -353,17 +353,17 @@ Widget::Widget( Ui::PostponeCall(this, [=] { listScrollUpdated(); }); }, lifetime()); - _filter->changes( + _search->changes( ) | rpl::start_with_next([=] { - applyFilterUpdate(); - }, _filter->lifetime()); - _filter->submits( - ) | rpl::start_with_next([=] { submit(); }, _filter->lifetime()); + applySearchUpdate(); + }, _search->lifetime()); + _search->submits( + ) | rpl::start_with_next([=] { submit(); }, _search->lifetime()); QObject::connect( - _filter->rawTextEdit().get(), + _search->rawTextEdit().get(), &QTextEdit::cursorPositionChanged, this, - [=] { filterCursorMoved(); }, + [=] { searchCursorMoved(); }, Qt::QueuedConnection); // So getLastText() works already. if (!Core::UpdaterDisabled()) { @@ -401,8 +401,8 @@ Widget::Widget( setupStories(); } - _searchForNarrowFilters->setClickedCallback([=] { - _filter->setFocusFast(); + _searchForNarrowLayout->setClickedCallback([=] { + _search->setFocusFast(); if (_childList) { controller->closeForum(); } @@ -440,8 +440,8 @@ Widget::Widget( loadMoreBlockedByDate(); }, lifetime()); - _filter->setFocusPolicy(Qt::StrongFocus); - _filter->customUpDown(true); + _search->setFocusPolicy(Qt::StrongFocus); + _search->customUpDown(true); updateJumpToDateVisibility(true); updateSearchFromVisibility(true); @@ -492,7 +492,7 @@ Widget::Widget( ) | rpl::start_with_next([=](float64 shown) { const auto color = (shown > 0.) ? &st::dialogsRippleBg : nullptr; _mainMenu.toggle->setRippleColorOverride(color); - _searchForNarrowFilters->setRippleColorOverride(color); + _searchForNarrowLayout->setRippleColorOverride(color); }, lifetime()); setupMoreChatsBar(); @@ -806,7 +806,7 @@ void Widget::setupMainMenuToggle() { const auto filtersHidden = !controller()->filtersWidth(); _mainMenu.toggle->setVisible(filtersHidden); _mainMenu.under->setVisible(filtersHidden); - _searchForNarrowFilters->setVisible(!filtersHidden); + _searchForNarrowLayout->setVisible(!filtersHidden); updateControlsGeometry(); }, lifetime()); @@ -1014,7 +1014,7 @@ void Widget::updateControlsVisibility(bool fast) { updateLoadMoreChatsVisibility(); _scroll->show(); updateStoriesVisibility(); - if ((_openedFolder || _openedForum) && _filter->hasFocus()) { + if ((_openedFolder || _openedForum) && _search->hasFocus()) { setInnerFocus(); } if (_updateTelegram) { @@ -1039,9 +1039,6 @@ void Widget::updateControlsVisibility(bool fast) { _forumReportBar->show(); } } else { - if (hasFocus() && !_childList) { - _filter->setFocusFast(); - } updateLockUnlockVisibility(); updateJumpToDateVisibility(fast); updateSearchFromVisibility(fast); @@ -1056,7 +1053,7 @@ void Widget::updateControlsVisibility(bool fast) { if (_hideChildListCanvas) { _hideChildListCanvas->show(); } - if (_childList && _filter->hasFocus()) { + if (_childList && _search->hasFocus()) { setInnerFocus(); } updateLockUnlockPosition(); @@ -1069,7 +1066,7 @@ void Widget::updateLockUnlockPosition() { const auto stories = (_stories && !_stories->isHidden()) ? _stories->collapsedGeometryCurrent() : Stories::List::CollapsedGeometry(); - const auto simple = _filter->x() + _filter->width(); + const auto simple = _search->x() + _search->width(); const auto right = stories.geometry.isEmpty() ? simple : anim::interpolate(stories.geometry.x(), simple, stories.expanded); @@ -1215,7 +1212,7 @@ void Widget::refreshTopBars() { }, _subsectionTopBar->lifetime()); _subsectionTopBar->searchQuery( ) | rpl::start_with_next([=](QString query) { - applyFilterUpdate(); + applySearchUpdate(); }, _subsectionTopBar->lifetime()); _subsectionTopBar->jumpToDateRequest( ) | rpl::start_with_next([=] { @@ -1381,9 +1378,12 @@ void Widget::checkUpdateStatus() { void Widget::setInnerFocus() { if (_childList) { _childList->setInnerFocus(); - } else if (!_openedFolder && !_openedForum) { - _filter->setFocus(); - } else if (!_subsectionTopBar->searchSetFocus()) { + } else if ((_openedFolder || _openedForum) + && _subsectionTopBar->searchSetFocus()) { + return; + } else if (!_search->getLastText().isEmpty() || _searchInChat) { + _search->setFocus(); + } else { setFocus(); } } @@ -1514,7 +1514,7 @@ void Widget::updateStoriesVisibility() { || _openedForum || !_widthAnimationCache.isNull() || _childList - || !_filter->getLastText().isEmpty() + || !_search->getLastText().isEmpty() || _searchInChat || _stories->empty(); if (_stories->isHidden() != hidden) { @@ -1631,7 +1631,7 @@ void Widget::slideFinished() { _shownProgressValue = 1.; updateControlsVisibility(true); if ((!_subsectionTopBar || !_subsectionTopBar->searchHasFocus()) - && !_filter->hasFocus()) { + && !_search->hasFocus()) { controller()->widget()->setInnerFocus(); } } @@ -1963,7 +1963,7 @@ void Widget::searchMessages(QString query, Key inChat) { setSearchInChat(inChat, nullptr, tags); } setSearchQuery(query); - applyFilterUpdate(true); + applySearchUpdate(true); _searchTimer.cancel(); searchMessages(); @@ -2433,7 +2433,7 @@ void Widget::listScrollUpdated() { _scrollToTop->update(); } -void Widget::applyFilterUpdate(bool force) { +void Widget::applySearchUpdate(bool force) { if (_showAnimation && !force) { return; } @@ -2462,13 +2462,13 @@ void Widget::applyFilterUpdate(bool force) { || _searchFromAuthor || !_searchTags.empty()) { auto switchToChooseFrom = HistoryView::SwitchToChooseFromQuery(); - if (_lastFilterText != switchToChooseFrom - && switchToChooseFrom.startsWith(_lastFilterText) + if (_lastSearchText != switchToChooseFrom + && switchToChooseFrom.startsWith(_lastSearchText) && filterText == switchToChooseFrom) { showSearchFrom(); } } - _lastFilterText = filterText; + _lastSearchText = filterText; } void Widget::showForum( @@ -2574,7 +2574,7 @@ void Widget::closeChildList(anim::type animated) { _childListShown = 0.; if (hasFocus()) { setInnerFocus(); - _filter->finishAnimating(); + _search->finishAnimating(); } if (animated == anim::type::normal) { _hideChildListCanvas->hide(); @@ -2673,7 +2673,7 @@ bool Widget::setSearchInChat( clearSearchCache(); _searchTags = std::move(list); if (_searchTags.empty()) { - applyFilterUpdate(true); + applySearchUpdate(true); } else { searchMessages(); } @@ -2684,10 +2684,14 @@ bool Widget::setSearchInChat( _openedForum && _searchInChat); } if (_searchFromAuthor - && _lastFilterText == HistoryView::SwitchToChooseFromQuery()) { + && _lastSearchText == HistoryView::SwitchToChooseFromQuery()) { cancelSearch(); } - _filter->setFocus(); + if (_searchInChat) { + _search->setFocus(); + } else { + setFocus(); + } return true; } @@ -2735,18 +2739,18 @@ void Widget::showSearchFrom() { } else if (const auto strong = weak.get()) { setSearchInChat(strong, from); } - applyFilterUpdate(true); + applySearchUpdate(true); }), - crl::guard(this, [=] { _filter->setFocus(); })); + crl::guard(this, [=] { _search->setFocus(); })); if (box) { controller()->show(std::move(box)); } } } -void Widget::filterCursorMoved() { - const auto to = _filter->textCursor().position(); - const auto text = _filter->getLastText(); +void Widget::searchCursorMoved() { + const auto to = _search->textCursor().position(); + const auto text = _search->getLastText(); auto hashtag = QStringView(); for (int start = to; start > 0;) { --start; @@ -2765,8 +2769,8 @@ void Widget::filterCursorMoved() { } void Widget::completeHashtag(QString tag) { - const auto t = _filter->getLastText(); - auto cur = _filter->textCursor().position(); + const auto t = _search->getLastText(); + auto cur = _search->textCursor().position(); auto hashtag = QString(); for (int start = cur; start > 0;) { --start; @@ -2781,9 +2785,9 @@ void Widget::completeHashtag(QString tag) { } if (cur - start - 1 == tag.size() && cur < t.size() && t.at(cur) == ' ') ++cur; hashtag = t.mid(0, start + 1) + tag + ' ' + t.mid(cur); - _filter->setText(hashtag); - _filter->setCursorPosition(start + 1 + tag.size() + 1); - applyFilterUpdate(true); + _search->setText(hashtag); + _search->setCursorPosition(start + 1 + tag.size() + 1); + applySearchUpdate(true); return; } break; @@ -2791,9 +2795,9 @@ void Widget::completeHashtag(QString tag) { break; } } - _filter->setText(t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur)); - _filter->setCursorPosition(cur + 1 + tag.size() + 1); - applyFilterUpdate(true); + _search->setText(t.mid(0, cur) + '#' + tag + ' ' + t.mid(cur)); + _search->setCursorPosition(cur + 1 + tag.size() + 1); + applySearchUpdate(true); } void Widget::resizeEvent(QResizeEvent *e) { @@ -2809,7 +2813,7 @@ void Widget::updateLockUnlockVisibility(anim::type animated) { || _openedForum || !_widthAnimationCache.isNull() || _childList - || !_filter->getLastText().isEmpty() + || !_search->getLastText().isEmpty() || _searchInChat; if (_lockUnlock->toggled() == hidden) { const auto stories = _stories && !_stories->empty(); @@ -2842,7 +2846,7 @@ void Widget::updateJumpToDateVisibility(bool fast) { } _jumpToDate->toggle( - (_searchInChat && _filter->getLastText().isEmpty()), + (_searchInChat && _search->getLastText().isEmpty()), fast ? anim::type::instant : anim::type::normal); } @@ -2864,7 +2868,7 @@ void Widget::updateSearchFromVisibility(bool fast) { if (visible) { additional.setRight(_chooseFromUser->width()); } - _filter->setAdditionalMargins(additional); + _search->setAdditionalMargins(additional); } } @@ -2898,9 +2902,9 @@ void Widget::updateControlsGeometry() { narrowRatio); } - auto filterTop = (filterAreaHeight - _filter->height()) / 2; + auto filterTop = (filterAreaHeight - _search->height()) / 2; filterLeft = anim::interpolate(filterLeft, _narrowWidth, narrowRatio); - _filter->setGeometryToLeft(filterLeft, filterTop, filterWidth, _filter->height()); + _search->setGeometryToLeft(filterLeft, filterTop, filterWidth, _search->height()); auto mainMenuLeft = anim::interpolate( st::dialogsFilterPadding.x(), @@ -2915,15 +2919,15 @@ void Widget::updateControlsGeometry() { + _mainMenu.toggle->height() + st::dialogsFilterPadding.y()); const auto searchLeft = anim::interpolate( - -_searchForNarrowFilters->width(), - (_narrowWidth - _searchForNarrowFilters->width()) / 2, + -_searchForNarrowLayout->width(), + (_narrowWidth - _searchForNarrowLayout->width()) / 2, narrowRatio); - _searchForNarrowFilters->moveToLeft(searchLeft, st::dialogsFilterPadding.y()); + _searchForNarrowLayout->moveToLeft(searchLeft, st::dialogsFilterPadding.y()); auto right = filterLeft + filterWidth; - _cancelSearch->moveToLeft(right - _cancelSearch->width(), _filter->y()); - right -= _jumpToDate->width(); _jumpToDate->moveToLeft(right, _filter->y()); - right -= _chooseFromUser->width(); _chooseFromUser->moveToLeft(right, _filter->y()); + _cancelSearch->moveToLeft(right - _cancelSearch->width(), _search->y()); + right -= _jumpToDate->width(); _jumpToDate->moveToLeft(right, _search->y()); + right -= _chooseFromUser->width(); _chooseFromUser->moveToLeft(right, _search->y()); const auto barw = width(); const auto expandedStoriesTop = filterAreaTop + filterAreaHeight; @@ -3066,6 +3070,14 @@ void Widget::keyPressEvent(QKeyEvent *e) { _inner->selectSkipPage(_scroll->height(), 1); } else if (e->key() == Qt::Key_PageUp) { _inner->selectSkipPage(_scroll->height(), -1); + } else if (!(e->modifiers() & ~Qt::ShiftModifier) + && e->key() != Qt::Key_Shift + && !_openedFolder + && !_openedForum + && _search->isVisible() + && !e->text().isEmpty()) { + _search->setFocusFast(); + QCoreApplication::sendEvent(_search->rawTextEdit(), e); } else { e->ignore(); } @@ -3132,14 +3144,14 @@ Data::ForumTopic *Widget::searchInTopic() const { QString Widget::currentSearchQuery() const { return _subsectionTopBar ? _subsectionTopBar->searchQueryCurrent() - : _filter->getLastText(); + : _search->getLastText(); } void Widget::clearSearchField() { if (_subsectionTopBar) { _subsectionTopBar->searchClear(); } else { - _filter->clear(); + _search->clear(); } } @@ -3147,7 +3159,7 @@ void Widget::setSearchQuery(const QString &query) { if (_subsectionTopBar) { _subsectionTopBar->searchSetText(query); } else { - _filter->setText(query); + _search->setText(query); } } @@ -3176,7 +3188,10 @@ bool Widget::cancelSearch() { _lastSearchId = _lastSearchMigratedId = 0; _inner->clearFilter(); clearSearchField(); - applyFilterUpdate(); + applySearchUpdate(); + if (!_searchInChat && _search->hasFocus()) { + setFocus(); + } return clearingQuery || clearingInChat; } @@ -3193,7 +3208,7 @@ void Widget::cancelSearchInChat() { } setSearchInChat(Key()); } - applyFilterUpdate(true); + applySearchUpdate(true); if (!isOneColumn) { controller()->content()->dialogsCancelled(); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index fc7264376..b3a53ee17 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -145,7 +145,7 @@ private: void chosenRow(const ChosenRow &row); void listScrollUpdated(); void cancelSearchInChat(); - void filterCursorMoved(); + void searchCursorMoved(); void completeHashtag(QString tag); [[nodiscard]] QString currentSearchQuery() const; @@ -223,7 +223,7 @@ private: void closeChildList(anim::type animated); void fullSearchRefreshOn(rpl::producer<> events); - void applyFilterUpdate(bool force = false); + void applySearchUpdate(bool force = false); void refreshLoadMoreButton(bool mayBlock, bool isBlocked); void loadMoreBlockedByDate(); @@ -255,8 +255,8 @@ private: object_ptr toggle; object_ptr under; } _mainMenu; - object_ptr _searchForNarrowFilters; - object_ptr _filter; + object_ptr _searchForNarrowLayout; + object_ptr _search; object_ptr> _chooseFromUser; object_ptr> _jumpToDate; object_ptr _cancelSearch; @@ -294,7 +294,7 @@ private: PeerData *_searchFromAuthor = nullptr; std::vector _searchTags; rpl::lifetime _searchTagsLifetime; - QString _lastFilterText; + QString _lastSearchText; rpl::event_stream> _storiesContents; base::flat_map _storiesUserpicsViewsHidden; diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 833ff1a96..581665aee 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1893,12 +1893,8 @@ int SessionController::minimalThreeColumnWidth() const { } bool SessionController::forceWideDialogs() const { - if (_dialogsListDisplayForced.current()) { - return true; - } else if (_dialogsListFocused.current()) { - return true; - } - return !content()->isMainSectionShown(); + return _dialogsListDisplayForced.current() + || _dialogsListFocused.current(); } auto SessionController::computeColumnLayout() const -> ColumnLayout { From 39658ffe5269a652fbf1f650a2dd08b43d68e0c0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 8 Apr 2024 12:13:25 +0400 Subject: [PATCH 014/175] Allow narrow chats width without main section. --- .../dialogs/dialogs_inner_widget.cpp | 5 ---- .../SourceFiles/dialogs/dialogs_widget.cpp | 18 +++++++++-- Telegram/SourceFiles/dialogs/dialogs_widget.h | 1 + Telegram/SourceFiles/mainwidget.cpp | 30 ++++--------------- .../window/window_lock_widgets.cpp | 3 -- .../window/window_session_controller.cpp | 5 ---- .../window/window_session_controller.h | 25 +++++----------- 7 files changed, 30 insertions(+), 57 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 0da825209..542d41118 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -2871,8 +2871,6 @@ void InnerWidget::refresh(bool toTop) { jumpToTop(); preloadRowsData(); } - _controller->setDialogsListDisplayForced( - _searchInChat || !_filter.isEmpty()); update(); } @@ -3060,9 +3058,6 @@ void InnerWidget::searchInChat( _searchInChatUserpic = {}; } moveCancelSearchButtons(); - - _controller->setDialogsListDisplayForced( - _searchInChat || !_filter.isEmpty()); } auto InnerWidget::searchTagsChanges() const diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index b2b1a8fe1..340a29471 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -357,8 +357,14 @@ Widget::Widget( ) | rpl::start_with_next([=] { applySearchUpdate(); }, _search->lifetime()); + + _search->focusedChanges() | rpl::start_with_next([=](bool focused) { + updateForceDisplayWide(); + }, _search->lifetime()); + _search->submits( ) | rpl::start_with_next([=] { submit(); }, _search->lifetime()); + QObject::connect( _search->rawTextEdit().get(), &QTextEdit::cursorPositionChanged, @@ -2469,6 +2475,13 @@ void Widget::applySearchUpdate(bool force) { } } _lastSearchText = filterText; + updateForceDisplayWide(); +} + +void Widget::updateForceDisplayWide() { + controller()->setChatsForceDisplayWide(_search->hasFocus() + || !_search->getLastText().isEmpty() + || _searchInChat); } void Widget::showForum( @@ -2687,11 +2700,12 @@ bool Widget::setSearchInChat( && _lastSearchText == HistoryView::SwitchToChooseFromQuery()) { cancelSearch(); } - if (_searchInChat) { + if (_searchInChat || !_search->getLastText().isEmpty()) { _search->setFocus(); } else { setFocus(); } + updateForceDisplayWide(); return true; } @@ -3209,7 +3223,7 @@ void Widget::cancelSearchInChat() { setSearchInChat(Key()); } applySearchUpdate(true); - if (!isOneColumn) { + if (!isOneColumn && _search->getLastText().isEmpty()) { controller()->content()->dialogsCancelled(); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index b3a53ee17..6686995f3 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -233,6 +233,7 @@ private: mtpRequestId requestId); void peopleFailed(const MTP::Error &error, mtpRequestId requestId); + void updateForceDisplayWide(); void scrollToDefault(bool verytop = false); void scrollToDefaultChecked(bool verytop = false); void setupScrollUpButton(); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index c1a6e1b2e..c60927950 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -296,11 +296,11 @@ MainWidget::MainWidget( _player->finishAnimating(); } - rpl::merge( - _controller->dialogsListFocusedChanges(), - _controller->dialogsListDisplayForcedChanges() + _controller->chatsForceDisplayWideChanges( ) | rpl::start_with_next([=] { - updateDialogsWidthAnimated(); + crl::on_main(this, [=] { + updateDialogsWidthAnimated(); + }); }, lifetime()); rpl::merge( @@ -1315,7 +1315,6 @@ void MainWidget::showHistory( } } - _controller->setDialogsListFocused(false); _a_dialogsWidth.stop(); using Way = SectionShow::Way; @@ -1708,7 +1707,6 @@ void MainWidget::showNewSection( _controller->window().hideSettingsAndLayer(); } - _controller->setDialogsListFocused(false); _a_dialogsWidth.stop(); auto mainSectionTop = getMainSectionTop(); @@ -2626,23 +2624,7 @@ bool MainWidget::eventFilter(QObject *o, QEvent *e) { const auto widget = o->isWidgetType() ? static_cast(o) : nullptr; - if (e->type() == QEvent::FocusIn) { - if (widget && (widget->window() == window())) { - if (_history == widget || _history->isAncestorOf(widget) - || (_mainSection - && (_mainSection == widget - || _mainSection->isAncestorOf(widget))) - || (_thirdSection - && (_thirdSection == widget - || _thirdSection->isAncestorOf(widget)))) { - _controller->setDialogsListFocused(false); - } else if (_dialogs - && (_dialogs == widget - || _dialogs->isAncestorOf(widget))) { - _controller->setDialogsListFocused(true); - } - } - } else if (e->type() == QEvent::MouseButtonPress) { + if (e->type() == QEvent::MouseButtonPress) { if (widget && (widget->window() == window())) { const auto event = static_cast(e); if (event->button() == Qt::BackButton) { @@ -2749,7 +2731,7 @@ void MainWidget::updateWindowAdaptiveLayout() { auto useSmallColumnWidth = !isOneColumn() && !dialogsWidthRatio - && !_controller->forceWideDialogs(); + && !_controller->chatsForceDisplayWide(); _dialogsWidth = !_dialogs ? 0 : useSmallColumnWidth diff --git a/Telegram/SourceFiles/window/window_lock_widgets.cpp b/Telegram/SourceFiles/window/window_lock_widgets.cpp index 813db805b..58f4881fa 100644 --- a/Telegram/SourceFiles/window/window_lock_widgets.cpp +++ b/Telegram/SourceFiles/window/window_lock_widgets.cpp @@ -41,9 +41,6 @@ not_null LockWidget::window() const { } void LockWidget::setInnerFocus() { - if (const auto controller = _window->sessionController()) { - controller->setDialogsListFocused(false); - } setFocus(); } diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 581665aee..7b47da42d 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1892,11 +1892,6 @@ int SessionController::minimalThreeColumnWidth() const { + st::columnMinimalWidthThird; } -bool SessionController::forceWideDialogs() const { - return _dialogsListDisplayForced.current() - || _dialogsListFocused.current(); -} - auto SessionController::computeColumnLayout() const -> ColumnLayout { auto layout = Adaptive::WindowLayout::OneColumn; diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 293dda20c..241567b5e 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -423,7 +423,6 @@ public: }; [[nodiscard]] ColumnLayout computeColumnLayout() const; int dialogsSmallColumnWidth() const; - bool forceWideDialogs() const; void updateColumnLayout() const; bool canShowThirdSection() const; bool canShowThirdSectionWithoutResize() const; @@ -516,24 +515,15 @@ public: std::optional show = std::nullopt) const; void finishChatThemeEdit(not_null peer); - [[nodiscard]] bool dialogsListFocused() const { - return _dialogsListFocused.current(); + [[nodiscard]] bool chatsForceDisplayWide() const { + return _chatsForceDisplayWide.current(); } - [[nodiscard]] rpl::producer dialogsListFocusedChanges() const { - return _dialogsListFocused.changes(); - } - void setDialogsListFocused(bool value) { - _dialogsListFocused = value; - } - [[nodiscard]] bool dialogsListDisplayForced() const { - return _dialogsListDisplayForced.current(); - } - [[nodiscard]] auto dialogsListDisplayForcedChanges() const + [[nodiscard]] auto chatsForceDisplayWideChanges() const -> rpl::producer { - return _dialogsListDisplayForced.changes(); + return _chatsForceDisplayWide.changes(); } - void setDialogsListDisplayForced(bool value) { - _dialogsListDisplayForced = value; + void setChatsForceDisplayWide(bool value) { + _chatsForceDisplayWide = value; } not_null parentController() override { @@ -688,8 +678,7 @@ private: rpl::variable _searchInChat; rpl::variable _activeChatEntry; rpl::lifetime _activeHistoryLifetime; - rpl::variable _dialogsListFocused = false; - rpl::variable _dialogsListDisplayForced = false; + rpl::variable _chatsForceDisplayWide = false; std::deque _chatEntryHistory; int _chatEntryHistoryPosition = -1; bool _filtersActivated = false; From 11e4c459695488f98fc2ee4915a1c0ce91dba310 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 8 Apr 2024 15:52:45 +0400 Subject: [PATCH 015/175] Don't collapse chats list on window unfocus. --- .../SourceFiles/dialogs/dialogs_widget.cpp | 22 +++++++++++++------ Telegram/SourceFiles/dialogs/dialogs_widget.h | 2 ++ Telegram/SourceFiles/mainwidget.cpp | 6 ++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 340a29471..e0afa642e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -358,8 +358,11 @@ Widget::Widget( applySearchUpdate(); }, _search->lifetime()); - _search->focusedChanges() | rpl::start_with_next([=](bool focused) { - updateForceDisplayWide(); + _search->focusedChanges( + ) | rpl::start_with_next([=](bool focused) { + if (focused) { + updateHasFocus(_search.data()); + } }, _search->lifetime()); _search->submits( @@ -1020,7 +1023,7 @@ void Widget::updateControlsVisibility(bool fast) { updateLoadMoreChatsVisibility(); _scroll->show(); updateStoriesVisibility(); - if ((_openedFolder || _openedForum) && _search->hasFocus()) { + if ((_openedFolder || _openedForum) && _searchHasFocus.current()) { setInnerFocus(); } if (_updateTelegram) { @@ -1059,7 +1062,7 @@ void Widget::updateControlsVisibility(bool fast) { if (_hideChildListCanvas) { _hideChildListCanvas->show(); } - if (_childList && _search->hasFocus()) { + if (_childList && _searchHasFocus.current()) { setInnerFocus(); } updateLockUnlockPosition(); @@ -1081,6 +1084,11 @@ void Widget::updateLockUnlockPosition() { st::dialogsFilterPadding.y()); } +void Widget::updateHasFocus(not_null focused) { + _searchHasFocus = (focused == _search.data()); + updateForceDisplayWide(); +} + void Widget::changeOpenedSubsection( FnMut change, bool fromRight, @@ -1637,7 +1645,7 @@ void Widget::slideFinished() { _shownProgressValue = 1.; updateControlsVisibility(true); if ((!_subsectionTopBar || !_subsectionTopBar->searchHasFocus()) - && !_search->hasFocus()) { + && !_searchHasFocus.current()) { controller()->widget()->setInnerFocus(); } } @@ -2479,7 +2487,7 @@ void Widget::applySearchUpdate(bool force) { } void Widget::updateForceDisplayWide() { - controller()->setChatsForceDisplayWide(_search->hasFocus() + controller()->setChatsForceDisplayWide(_searchHasFocus.current() || !_search->getLastText().isEmpty() || _searchInChat); } @@ -3203,7 +3211,7 @@ bool Widget::cancelSearch() { _inner->clearFilter(); clearSearchField(); applySearchUpdate(); - if (!_searchInChat && _search->hasFocus()) { + if (!_searchInChat && _searchHasFocus.current()) { setFocus(); } return clearingQuery || clearingInChat; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index 6686995f3..f3ebbd715 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -123,6 +123,7 @@ public: [[nodiscard]] RowDescriptor resolveChatNext(RowDescriptor from = {}) const; [[nodiscard]] RowDescriptor resolveChatPrevious(RowDescriptor from = {}) const; + void updateHasFocus(not_null focused); // Float player interface. bool floatPlayerHandleWheelEvent(QEvent *e) override; @@ -296,6 +297,7 @@ private: std::vector _searchTags; rpl::lifetime _searchTagsLifetime; QString _lastSearchText; + rpl::variable _searchHasFocus = false; rpl::event_stream> _storiesContents; base::flat_map _storiesUserpicsViewsHidden; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index c60927950..debb7490f 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2624,7 +2624,11 @@ bool MainWidget::eventFilter(QObject *o, QEvent *e) { const auto widget = o->isWidgetType() ? static_cast(o) : nullptr; - if (e->type() == QEvent::MouseButtonPress) { + if (e->type() == QEvent::FocusIn) { + if (widget && _dialogs && widget->window() == window()) { + _dialogs->updateHasFocus(widget); + } + } else if (e->type() == QEvent::MouseButtonPress) { if (widget && (widget->window() == window())) { const auto event = static_cast(e); if (event->button() == Qt::BackButton) { From 18598f8dcaaa28fcaf74fdc8a488df4be6c82aee Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Apr 2024 19:04:18 +0400 Subject: [PATCH 016/175] Initial version of top peers. --- Telegram/CMakeLists.txt | 26 +- Telegram/SourceFiles/api/api_updates.cpp | 6 + .../data/components/recent_peers.cpp | 18 ++ .../data/components/recent_peers.h | 26 ++ .../SourceFiles/data/components/top_peers.cpp | 157 +++++++++ .../SourceFiles/data/components/top_peers.h | 51 +++ Telegram/SourceFiles/dialogs/dialogs.style | 6 + .../SourceFiles/dialogs/dialogs_widget.cpp | 55 +++- Telegram/SourceFiles/dialogs/dialogs_widget.h | 5 +- .../dialogs/ui/dialogs_stories_list.cpp | 12 +- .../dialogs/ui/dialogs_stories_list.h | 6 +- .../dialogs/ui/dialogs_suggestions.cpp | 77 +++++ .../dialogs/ui/dialogs_suggestions.h | 59 ++++ .../dialogs/ui/top_peers_strip.cpp | 302 ++++++++++++++++++ .../SourceFiles/dialogs/ui/top_peers_strip.h | 93 ++++++ Telegram/SourceFiles/history/history.cpp | 7 +- Telegram/SourceFiles/main/main_session.cpp | 3 + Telegram/SourceFiles/main/main_session.h | 10 + .../SourceFiles/mtproto/mtproto_config.cpp | 13 +- Telegram/SourceFiles/mtproto/mtproto_config.h | 1 + Telegram/cmake/td_ui.cmake | 2 + 21 files changed, 908 insertions(+), 27 deletions(-) create mode 100644 Telegram/SourceFiles/data/components/recent_peers.cpp create mode 100644 Telegram/SourceFiles/data/components/recent_peers.h create mode 100644 Telegram/SourceFiles/data/components/top_peers.cpp create mode 100644 Telegram/SourceFiles/data/components/top_peers.h create mode 100644 Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp create mode 100644 Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h create mode 100644 Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp create mode 100644 Telegram/SourceFiles/dialogs/ui/top_peers_strip.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index baf77c58c..0ffd19f99 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -460,10 +460,14 @@ PRIVATE data/business/data_business_info.h data/business/data_shortcut_messages.cpp data/business/data_shortcut_messages.h + data/components/recent_peers.cpp + data/components/recent_peers.h data/components/scheduled_messages.cpp data/components/scheduled_messages.h data/components/sponsored_messages.cpp data/components/sponsored_messages.h + data/components/top_peers.cpp + data/components/top_peers.h data/notify/data_notify_settings.cpp data/notify/data_notify_settings.h data/notify/data_peer_notify_settings.cpp @@ -608,6 +612,18 @@ PRIVATE data/data_wall_paper.h data/data_web_page.cpp data/data_web_page.h + dialogs/ui/dialogs_layout.cpp + dialogs/ui/dialogs_layout.h + dialogs/ui/dialogs_message_view.cpp + dialogs/ui/dialogs_message_view.h + dialogs/ui/dialogs_stories_content.cpp + dialogs/ui/dialogs_stories_content.h + dialogs/ui/dialogs_suggestions.cpp + dialogs/ui/dialogs_suggestions.h + dialogs/ui/dialogs_topics_view.cpp + dialogs/ui/dialogs_topics_view.h + dialogs/ui/dialogs_video_userpic.cpp + dialogs/ui/dialogs_video_userpic.h dialogs/dialogs_entry.cpp dialogs/dialogs_entry.h dialogs/dialogs_indexed_list.cpp @@ -630,16 +646,6 @@ PRIVATE dialogs/dialogs_search_tags.h dialogs/dialogs_widget.cpp dialogs/dialogs_widget.h - dialogs/ui/dialogs_layout.cpp - dialogs/ui/dialogs_layout.h - dialogs/ui/dialogs_message_view.cpp - dialogs/ui/dialogs_message_view.h - dialogs/ui/dialogs_stories_content.cpp - dialogs/ui/dialogs_stories_content.h - dialogs/ui/dialogs_topics_view.cpp - dialogs/ui/dialogs_topics_view.h - dialogs/ui/dialogs_video_userpic.cpp - dialogs/ui/dialogs_video_userpic.h editor/color_picker.cpp editor/color_picker.h editor/controllers/controllers.h diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index 9918ec7a9..314e6c862 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_dc_options.h" #include "data/business/data_shortcut_messages.h" #include "data/components/scheduled_messages.h" +#include "data/components/top_peers.h" #include "data/notify/data_notify_settings.h" #include "data/stickers/data_stickers.h" #include "data/data_saved_messages.h" @@ -1577,6 +1578,11 @@ void Updates::feedUpdate(const MTPUpdate &update) { } else { if (existing) { existing->destroy(); + } else { + // Not the server-side date, but close enough. + session().topPeers().increment( + local->history()->peer, + local->date()); } local->setRealId(d.vid().v); } diff --git a/Telegram/SourceFiles/data/components/recent_peers.cpp b/Telegram/SourceFiles/data/components/recent_peers.cpp new file mode 100644 index 000000000..32fc0ad42 --- /dev/null +++ b/Telegram/SourceFiles/data/components/recent_peers.cpp @@ -0,0 +1,18 @@ +/* +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/components/recent_peers.h" + +namespace Data { + +RecentPeers::RecentPeers(not_null session) +: _session(session) { +} + +RecentPeers::~RecentPeers() = default; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/components/recent_peers.h b/Telegram/SourceFiles/data/components/recent_peers.h new file mode 100644 index 000000000..e3a648b47 --- /dev/null +++ b/Telegram/SourceFiles/data/components/recent_peers.h @@ -0,0 +1,26 @@ +/* +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 Main { +class Session; +} // namespace Main + +namespace Data { + +class RecentPeers final { +public: + explicit RecentPeers(not_null session); + ~RecentPeers(); + +private: + const not_null _session; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/components/top_peers.cpp b/Telegram/SourceFiles/data/components/top_peers.cpp new file mode 100644 index 000000000..253c716f3 --- /dev/null +++ b/Telegram/SourceFiles/data/components/top_peers.cpp @@ -0,0 +1,157 @@ +/* +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/components/top_peers.h" + +#include "api/api_hash.h" +#include "apiwrap.h" +#include "data/data_peer.h" +#include "data/data_session.h" +#include "data/data_user.h" +#include "main/main_session.h" +#include "mtproto/mtproto_config.h" + +namespace Data { +namespace { + +constexpr auto kLimit = 32; +constexpr auto kRequestTimeLimit = 10 * crl::time(1000); + +[[nodiscard]] float64 RatingDelta(TimeId now, TimeId was, int decay) { + return std::exp((now - was) * 1. / decay); +} + +} // namespace + +TopPeers::TopPeers(not_null session) +: _session(session) { + using namespace rpl::mappers; + crl::on_main(session, [=] { + _session->data().chatsListLoadedEvents( + ) | rpl::filter(_1 == nullptr) | rpl::start_with_next([=] { + crl::on_main(_session, [=] { + request(); + }); + }, _session->lifetime()); + }); +} + +TopPeers::~TopPeers() = default; + +std::vector> TopPeers::list() const { + return _list + | ranges::view::transform(&TopPeer::peer) + | ranges::to_vector; +} + +bool TopPeers::disabled() const { + return _disabled; +} + +rpl::producer<> TopPeers::updates() const { + return _updates.events(); +} + +void TopPeers::increment(not_null peer, TimeId date) { + if (date <= _lastReceivedDate) { + return; + } + if (const auto user = peer->asUser(); user && !user->isBot()) { + auto changed = false; + auto i = ranges::find(_list, peer, &TopPeer::peer); + if (i == end(_list)) { + _list.push_back({ .peer = peer }); + i = end(_list) - 1; + changed = true; + } + const auto &config = peer->session().mtp().config(); + const auto decay = config.values().ratingDecay; + i->rating += RatingDelta(date, _lastReceivedDate, decay); + for (; i != begin(_list); --i) { + if (i->rating >= (i - 1)->rating) { + changed = true; + std::swap(*i, *(i - 1)); + } else { + break; + } + } + if (changed) { + _updates.fire({}); + } + } +} + +void TopPeers::reload() { + if (_requestId + || (_lastReceived + && _lastReceived + kRequestTimeLimit > crl::now())) { + return; + } + request(); +} + +void TopPeers::request() { + if (_requestId) { + return; + } + + _requestId = _session->api().request(MTPcontacts_GetTopPeers( + MTP_flags(MTPcontacts_GetTopPeers::Flag::f_correspondents), + MTP_int(0), + MTP_int(kLimit), + MTP_long(countHash()) + )).done([=](const MTPcontacts_TopPeers &result, const MTP::Response &response) { + _lastReceivedDate = TimeId(response.outerMsgId >> 32); + _lastReceived = crl::now(); + _requestId = 0; + + result.match([&](const MTPDcontacts_topPeers &data) { + _disabled = false; + const auto owner = &_session->data(); + owner->processUsers(data.vusers()); + owner->processChats(data.vchats()); + for (const auto &category : data.vcategories().v) { + const auto &data = category.data(); + data.vcategory().match( + [&](const MTPDtopPeerCategoryCorrespondents &) { + _list = ranges::views::all( + data.vpeers().v + ) | ranges::views::transform([&](const MTPTopPeer &top) { + return TopPeer{ + owner->peer(peerFromMTP(top.data().vpeer())), + top.data().vrating().v, + }; + }) | ranges::to_vector; + }, [](const auto &) { + LOG(("API Error: Unexpected top peer category.")); + }); + } + _updates.fire({}); + }, [&](const MTPDcontacts_topPeersDisabled &) { + if (!_disabled) { + _list.clear(); + _disabled = true; + _updates.fire({}); + } + }, [](const MTPDcontacts_topPeersNotModified &) { + }); + }).fail([=] { + _lastReceived = crl::now(); + _requestId = 0; + }).send(); +} + +uint64 TopPeers::countHash() const { + using namespace Api; + auto hash = HashInit(); + for (const auto &top : _list) { + HashUpdate(hash, peerToUser(top.peer->id).bare); + } + return HashFinalize(hash); +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/components/top_peers.h b/Telegram/SourceFiles/data/components/top_peers.h new file mode 100644 index 000000000..b5107381c --- /dev/null +++ b/Telegram/SourceFiles/data/components/top_peers.h @@ -0,0 +1,51 @@ +/* +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 Main { +class Session; +} // namespace Main + +namespace Data { + +class TopPeers final { +public: + explicit TopPeers(not_null session); + ~TopPeers(); + + [[nodiscard]] std::vector> list() const; + [[nodiscard]] bool disabled() const; + [[nodiscard]] rpl::producer<> updates() const; + + void increment(not_null peer, TimeId date); + void reload(); + +private: + struct TopPeer { + not_null peer; + float64 rating = 0.; + }; + + void request(); + [[nodiscard]] uint64 countHash() const; + + const not_null _session; + + std::vector _list; + rpl::event_stream<> _updates; + crl::time _lastReceived = 0; + TimeId _lastReceivedDate = 0; + + mtpRequestId _requestId = 0; + + bool _disabled = false; + bool _received = false; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 224417f20..d0d340b09 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -591,6 +591,11 @@ dialogsStoriesFull: DialogsStories { font: font(11px); } } +topPeers: DialogsStories(dialogsStoriesFull) { + photo: 46px; + photoLeft: 10px; + photoTop: 8px; +} dialogsStoriesList: DialogsStoriesList { small: dialogsStories; @@ -633,3 +638,4 @@ dialogsSearchTagArrowPadding: margins(-6px, 3px, 0px, 0px); dialogsSearchTagPromoLeft: 6px; dialogsSearchTagPromoRight: 1px; dialogsSearchTagPromoSkip: 6px; + diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index e0afa642e..f60781b44 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/options.h" #include "dialogs/ui/dialogs_stories_content.h" #include "dialogs/ui/dialogs_stories_list.h" +#include "dialogs/ui/dialogs_suggestions.h" #include "dialogs/dialogs_inner_widget.h" #include "dialogs/dialogs_search_from_controllers.h" #include "dialogs/dialogs_key.h" @@ -1021,7 +1022,10 @@ void Widget::fullSearchRefreshOn(rpl::producer<> events) { void Widget::updateControlsVisibility(bool fast) { updateLoadMoreChatsVisibility(); - _scroll->show(); + _scroll->setVisible(!_suggestions); + if (_suggestions) { + _suggestions->show(); + } updateStoriesVisibility(); if ((_openedFolder || _openedForum) && _searchHasFocus.current()) { setInnerFocus(); @@ -1085,8 +1089,36 @@ void Widget::updateLockUnlockPosition() { } void Widget::updateHasFocus(not_null focused) { - _searchHasFocus = (focused == _search.data()); - updateForceDisplayWide(); + const auto has = (focused == _search.data()); + if (_searchHasFocus.current() != has) { + _searchHasFocus = (focused == _search.data()); + updateStoriesVisibility(); + updateForceDisplayWide(); + updateSuggestions(anim::type::normal); + } +} + +void Widget::updateSuggestions(anim::type animated) { + const auto suggest = _searchHasFocus.current() + && !_searchInChat + && (_inner->state() == WidgetState::Default); + if (!suggest && _suggestions) { + _suggestions = nullptr; + _scroll->show(); + } else if (suggest && !_suggestions) { + _suggestions = std::make_unique( + this, + rpl::single(TopPeersContent(&session()))); + + _suggestions->topPeerChosen( + ) | rpl::start_with_next([=](PeerId id) { + controller()->showPeerHistory(id); + }, _suggestions->lifetime()); + + _suggestions->show(); + _scroll->hide(); + updateControlsGeometry(); + } } void Widget::changeOpenedSubsection( @@ -1513,7 +1545,10 @@ void Widget::startWidthAnimation() { void Widget::stopWidthAnimation() { _widthAnimationCache = QPixmap(); if (!_showAnimation) { - _scroll->show(); + _scroll->setVisible(!_suggestions); + if (_suggestions) { + _suggestions->show(); + } } updateStoriesVisibility(); update(); @@ -1528,6 +1563,7 @@ void Widget::updateStoriesVisibility() { || _openedForum || !_widthAnimationCache.isNull() || _childList + || _searchHasFocus.current() || !_search->getLastText().isEmpty() || _searchInChat || _stories->empty(); @@ -2460,6 +2496,7 @@ void Widget::applySearchUpdate(bool force) { clearSearchCache(); } _cancelSearch->toggle(!filterText.isEmpty(), anim::type::normal); + updateSuggestions(anim::type::instant); updateLoadMoreChatsVisibility(); updateJumpToDateVisibility(); updateLockUnlockPosition(); @@ -2675,6 +2712,7 @@ bool Widget::setSearchInChat( if (searchInPeerUpdated) { _searchInChat = chat; controller()->setSearchInChat(_searchInChat); + updateSuggestions(anim::type::instant); updateJumpToDateVisibility(); updateStoriesVisibility(); } @@ -3041,6 +3079,14 @@ void Widget::updateControlsGeometry() { }; _updateScrollGeometryCached(); + if (_suggestions) { + _suggestions->setGeometry( + 0, + expandedStoriesTop, + scrollWidth, + height() - expandedStoriesTop - bottomSkip); + } + _inner->resize(scrollWidth, _inner->height()); _inner->setNarrowRatio(narrowRatio); if (newScrollTop != wasScrollTop) { @@ -3097,6 +3143,7 @@ void Widget::keyPressEvent(QKeyEvent *e) { && !_openedFolder && !_openedForum && _search->isVisible() + && !_search->hasFocus() && !e->text().isEmpty()) { _search->setFocusFast(); QCoreApplication::sendEvent(_search->rawTextEdit(), e); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index f3ebbd715..f3b54aa49 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -75,6 +75,7 @@ class Key; struct ChosenRow; class InnerWidget; enum class SearchRequestType; +class Suggestions; class Widget final : public Window::AbstractSectionWidget { public: @@ -242,6 +243,7 @@ private: void startScrollUpButtonAnimation(bool shown); void updateScrollUpPosition(); void updateLockUnlockPosition(); + void updateSuggestions(anim::type animated); MTP::Sender _api; @@ -273,6 +275,7 @@ private: object_ptr _scroll; QPointer _inner; + std::unique_ptr _suggestions; class BottomButton; object_ptr _updateTelegram = { nullptr }; object_ptr _loadMoreChats = { nullptr }; @@ -291,7 +294,7 @@ private: Data::Folder *_openedFolder = nullptr; Data::Forum *_openedForum = nullptr; - Dialogs::Key _searchInChat; + Key _searchInChat; History *_searchInMigrated = nullptr; PeerData *_searchFromAuthor = nullptr; std::vector _searchTags; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp index f615f4fb3..1e4a4cd5d 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.cpp @@ -184,9 +184,9 @@ rpl::producer List::toggleExpandedRequests() const { return _toggleExpandedRequests.events(); } -rpl::producer<> List::entered() const { - return _entered.events(); -} +//rpl::producer<> List::entered() const { +// return _entered.events(); +//} rpl::producer<> List::loadMoreRequests() const { return _loadMoreRequests.events(); @@ -217,9 +217,9 @@ void List::requestExpanded(bool expanded) { _toggleExpandedRequests.fire_copy(_expanded); } -void List::enterEventHook(QEnterEvent *e) { - _entered.fire({}); -} +//void List::enterEventHook(QEnterEvent *e) { + //_entered.fire({}); +//} void List::resizeEvent(QResizeEvent *e) { updateScrollMax(); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h index 869767745..303938957 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_stories_list.h @@ -94,7 +94,7 @@ public: [[nodiscard]] rpl::producer clicks() const; [[nodiscard]] rpl::producer showMenuRequests() const; [[nodiscard]] rpl::producer toggleExpandedRequests() const; - [[nodiscard]] rpl::producer<> entered() const; + //[[nodiscard]] rpl::producer<> entered() const; [[nodiscard]] rpl::producer<> loadMoreRequests() const; [[nodiscard]] auto verticalScrollEvents() const @@ -123,7 +123,7 @@ private: }; void showContent(Content &&content); - void enterEventHook(QEnterEvent *e) override; + //void enterEventHook(QEnterEvent *e) override; void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; void wheelEvent(QWheelEvent *e) override; @@ -173,7 +173,7 @@ private: rpl::event_stream _clicks; rpl::event_stream _showMenuRequests; rpl::event_stream _toggleExpandedRequests; - rpl::event_stream<> _entered; + //rpl::event_stream<> _entered; rpl::event_stream<> _loadMoreRequests; rpl::event_stream<> _collapsedGeometryChanged; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp new file mode 100644 index 000000000..9c9e90bc4 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -0,0 +1,77 @@ +/* +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 "dialogs/ui/dialogs_suggestions.h" + +#include "data/components/top_peers.h" +#include "data/data_user.h" +#include "main/main_session.h" +#include "ui/widgets/elastic_scroll.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/dynamic_thumbnails.h" +#include "styles/style_layers.h" + +namespace Dialogs { + +Suggestions::Suggestions( + not_null parent, + rpl::producer topPeers) +: RpWidget(parent) +, _scroll(std::make_unique(this)) +, _content(_scroll->setOwnedWidget(object_ptr(this))) +, _topPeersWrap(_content->add(object_ptr>( + this, + object_ptr(this, std::move(topPeers))))) +, _topPeers(_topPeersWrap->entity()) +, _divider(_content->add(setupDivider())) { + _topPeers->emptyValue() | rpl::start_with_next([=](bool empty) { + _topPeersWrap->toggle(!empty, anim::type::instant); + }, _topPeers->lifetime()); + + _topPeers->clicks() | rpl::start_with_next([=](uint64 peerIdRaw) { + _topPeerChosen.fire(PeerId(peerIdRaw)); + }, _topPeers->lifetime()); +} + +Suggestions::~Suggestions() = default; + +void Suggestions::paintEvent(QPaintEvent *e) { + QPainter(this).fillRect(e->rect(), st::windowBg); +} + +void Suggestions::resizeEvent(QResizeEvent *e) { + _scroll->setGeometry(rect()); + _content->resizeToWidth(width()); +} + +object_ptr Suggestions::setupDivider() { + auto result = object_ptr( + this, + object_ptr( + this, + rpl::single(u"Recent"_q), + st::boxDividerLabel), + st::defaultBoxDividerLabelPadding); + + return result; +} + +TopPeersList TopPeersContent(not_null session) { + auto result = TopPeersList(); + for (const auto &peer : session->topPeers().list()) { + result.entries.push_back(TopPeersEntry{ + .id = peer->id.value, + .name = peer->shortName(), + .userpic = Ui::MakeUserpicThumbnail(peer), + }); + } + return result; +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h new file mode 100644 index 000000000..10622576a --- /dev/null +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h @@ -0,0 +1,59 @@ +/* +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 "dialogs/ui/top_peers_strip.h" +#include "ui/rp_widget.h" + +namespace Main { +class Session; +} // namespace Main + +namespace Ui { +class ElasticScroll; +class VerticalLayout; +template +class SlideWrap; +} // namespace Ui + +namespace Dialogs { + +class Suggestions final : public Ui::RpWidget { +public: + Suggestions( + not_null parent, + rpl::producer topPeers); + ~Suggestions(); + + [[nodiscard]] rpl::producer topPeerChosen() const { + return _topPeerChosen.events(); + } + +private: + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + + [[nodiscard]] object_ptr setupDivider(); + + void updateControlsGeometry(); + + const std::unique_ptr _scroll; + const not_null _content; + const not_null*> _topPeersWrap; + const not_null _topPeers; + const not_null _divider; + + rpl::event_stream _topPeerChosen; + +}; + +[[nodiscard]] TopPeersList TopPeersContent( + not_null session); + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp new file mode 100644 index 000000000..65adee797 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -0,0 +1,302 @@ +/* +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 "dialogs/ui/top_peers_strip.h" + +#include "ui/text/text.h" +#include "ui/widgets/menu/menu_add_action_callback_factory.h" +#include "ui/widgets/popup_menu.h" +#include "ui/dynamic_image.h" +#include "ui/painter.h" +#include "styles/style_dialogs.h" +#include "styles/style_widgets.h" + +#include + +namespace Dialogs { + +struct TopPeersStrip::Entry { + uint64 id = 0; + Ui::Text::String name; + std::shared_ptr userpic; + bool subscribed = false; +}; + +TopPeersStrip::TopPeersStrip( + not_null parent, + rpl::producer content) +: RpWidget(parent) { + resize(0, st::topPeers.height); + + std::move(content) | rpl::start_with_next([=](const TopPeersList &list) { + apply(list); + }, lifetime()); + + setMouseTracking(true); +} + +TopPeersStrip::~TopPeersStrip() = default; + +void TopPeersStrip::resizeEvent(QResizeEvent *e) { + updateScrollMax(); +} + +void TopPeersStrip::wheelEvent(QWheelEvent *e) { + const auto phase = e->phase(); + const auto fullDelta = e->pixelDelta().isNull() + ? e->angleDelta() + : e->pixelDelta(); + if (phase == Qt::ScrollBegin || phase == Qt::ScrollEnd) { + _scrollingLock = Qt::Orientation(); + if (fullDelta.isNull()) { + return; + } + } + const auto vertical = qAbs(fullDelta.x()) < qAbs(fullDelta.y()); + if (_scrollingLock == Qt::Orientation() && phase != Qt::NoScrollPhase) { + _scrollingLock = vertical ? Qt::Vertical : Qt::Horizontal; + } + if (_scrollingLock == Qt::Vertical || (vertical && !_scrollLeftMax)) { + _verticalScrollEvents.fire(e); + return; + } + const auto delta = vertical + ? fullDelta.y() + : ((style::RightToLeft() ? -1 : 1) * fullDelta.x()); + + const auto now = _scrollLeft; + const auto used = now - delta; + const auto next = std::clamp(used, 0, _scrollLeftMax); + if (next != now) { + _scrollLeft = next; + updateSelected(); + update(); + } + e->accept(); +} + +void TopPeersStrip::mousePressEvent(QMouseEvent *e) { + if (e->button() != Qt::LeftButton) { + return; + } + _lastMousePosition = e->globalPos(); + updateSelected(); + + _mouseDownPosition = _lastMousePosition; + _pressed = _selected; +} + +void TopPeersStrip::mouseMoveEvent(QMouseEvent *e) { + _lastMousePosition = e->globalPos(); + updateSelected(); + + if (!_dragging && _mouseDownPosition) { + if ((_lastMousePosition - *_mouseDownPosition).manhattanLength() + >= QApplication::startDragDistance()) { + _dragging = true; + _startDraggingLeft = _scrollLeft; + } + } + checkDragging(); +} + +void TopPeersStrip::checkDragging() { + if (_dragging) { + const auto sign = (style::RightToLeft() ? -1 : 1); + const auto newLeft = std::clamp( + (sign * (_mouseDownPosition->x() - _lastMousePosition.x()) + + _startDraggingLeft), + 0, + _scrollLeftMax); + if (newLeft != _scrollLeft) { + _scrollLeft = newLeft; + update(); + } + } +} + +void TopPeersStrip::mouseReleaseEvent(QMouseEvent *e) { + _lastMousePosition = e->globalPos(); + const auto guard = gsl::finally([&] { + _mouseDownPosition = std::nullopt; + }); + + const auto pressed = std::exchange(_pressed, -1); + if (finishDragging()) { + return; + } + updateSelected(); + if (_selected == pressed) { + if (_selected < _entries.size()) { + _clicks.fire_copy(_entries[_selected].id); + } + } +} + +void TopPeersStrip::updateScrollMax() { + const auto &st = st::topPeers; + const auto single = st.photoLeft * 2 + st.photo; + const auto widthFull = int(_entries.size()) * single; + _scrollLeftMax = std::max(widthFull - width(), 0); + _scrollLeft = std::clamp(_scrollLeft, 0, _scrollLeftMax); + update(); +} + +bool TopPeersStrip::empty() const { + return _empty.current(); +} + +rpl::producer TopPeersStrip::emptyValue() const { + return _empty.value(); +} + +rpl::producer TopPeersStrip::clicks() const { + return _clicks.events(); +} + +auto TopPeersStrip::showMenuRequests() const +-> rpl::producer { + return _showMenuRequests.events(); +} + +void TopPeersStrip::apply(const TopPeersList &list) { + auto now = std::vector(); + + if (list.entries.empty()) { + _empty = true; + } + for (const auto &entry : list.entries) { + const auto i = ranges::find(_entries, entry.id, &Entry::id); + if (i != end(_entries)) { + now.push_back(base::take(*i)); + } else { + now.push_back({ .id = entry.id }); + } + apply(now.back(), entry); + } + for (auto &entry : _entries) { + if (entry.subscribed) { + entry.userpic->subscribeToUpdates(nullptr); + entry.subscribed = 0; + } + } + _entries = std::move(now); + if (!_entries.empty()) { + _empty = false; + } + update(); +} + +void TopPeersStrip::apply(Entry &entry, const TopPeersEntry &data) { + Expects(entry.id == data.id); + Expects(data.userpic != nullptr); + + if (entry.name.toString() != data.name) { + entry.name.setText(st::topPeers.nameStyle, data.name); + } + if (entry.userpic.get() != data.userpic.get()) { + if (entry.subscribed) { + entry.userpic->subscribeToUpdates(nullptr); + entry.subscribed = 0; + } + entry.userpic = data.userpic; + } +} + +void TopPeersStrip::paintEvent(QPaintEvent *e) { + auto p = Painter(this); + auto x = -_scrollLeft; + const auto &st = st::topPeers; + const auto line = st.lineTwice / 2; + const auto single = st.photoLeft * 2 + st.photo; + for (auto &entry : _entries) { + if (!entry.subscribed) { + entry.userpic->subscribeToUpdates([=] { + update(); + }); + entry.subscribed = 1; + } + const auto image = entry.userpic->image(st.photo); + p.drawImage( + QRect(x + st.photoLeft, st.photoTop, st.photo, st.photo), + image); + + const auto nameLeft = x + st.nameLeft; + entry.name.drawElided(p, nameLeft, st.nameTop, single, 1, style::al_top); + + x += single; + } +} + +void TopPeersStrip::contextMenuEvent(QContextMenuEvent *e) { + _menu = nullptr; + + if (e->reason() == QContextMenuEvent::Mouse) { + _lastMousePosition = e->globalPos(); + updateSelected(); + } + if (_selected < 0 || _entries.empty()) { + return; + } + _menu = base::make_unique_q( + this, + st::popupMenuWithIcons); + _showMenuRequests.fire({ + _entries[_selected].id, + Ui::Menu::CreateAddActionCallback(_menu), + }); + if (_menu->empty()) { + _menu = nullptr; + return; + } + const auto updateAfterMenuDestroyed = [=] { + const auto globalPosition = QCursor::pos(); + if (rect().contains(mapFromGlobal(globalPosition))) { + _lastMousePosition = globalPosition; + updateSelected(); + } + }; + QObject::connect( + _menu.get(), + &QObject::destroyed, + crl::guard(&_menuGuard, updateAfterMenuDestroyed)); + _menu->popup(e->globalPos()); + e->accept(); +} + +bool TopPeersStrip::finishDragging() { + if (!_dragging) { + return false; + } + checkDragging(); + _dragging = false; + updateSelected(); + return true; +} + +void TopPeersStrip::updateSelected() { + if (_pressed >= 0) { + return; + } + const auto &st = st::topPeers; + const auto p = mapFromGlobal(_lastMousePosition); + const auto x = p.x(); + const auto single = st.photoLeft * 2 + st.photo; + const auto index = (x - _scrollLeft) / single; + const auto selected = (index < 0 || index >= _entries.size()) + ? -1 + : index; + if (_selected != selected) { + const auto over = (selected >= 0); + if (over != (_selected >= 0)) { + setCursor(over ? style::cur_pointer : style::cur_default); + } + _selected = selected; + } +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h new file mode 100644 index 000000000..551b82480 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -0,0 +1,93 @@ +/* +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/weak_ptr.h" +#include "ui/widgets/menu/menu_add_action_callback.h" +#include "ui/rp_widget.h" + +namespace Ui { +class DynamicImage; +} // namespace Ui + +namespace Dialogs { + +struct TopPeersEntry { + uint64 id = 0; + QString name; + std::shared_ptr userpic; + uint32 badge : 30 = 0; + uint32 muted : 1 = 0; + uint32 online : 1 = 0; +}; + +struct TopPeersList { + std::vector entries; +}; + +struct ShowTopPeerMenuRequest { + uint64 id = 0; + Ui::Menu::MenuCallback callback; +}; + +class TopPeersStrip final : public Ui::RpWidget { +public: + TopPeersStrip( + not_null parent, + rpl::producer content); + ~TopPeersStrip(); + + [[nodiscard]] bool empty() const; + [[nodiscard]] rpl::producer emptyValue() const; + [[nodiscard]] rpl::producer clicks() const; + [[nodiscard]] auto showMenuRequests() const + -> rpl::producer; + +private: + struct Entry; + + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void wheelEvent(QWheelEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + void contextMenuEvent(QContextMenuEvent *e) override; + + void updateScrollMax(); + void updateSelected(); + void checkDragging(); + bool finishDragging(); + + void apply(const TopPeersList &list); + void apply(Entry &entry, const TopPeersEntry &data); + + std::vector _entries; + rpl::variable _empty = true; + + rpl::event_stream _clicks; + rpl::event_stream _showMenuRequests; + rpl::event_stream> _verticalScrollEvents; + + QPoint _lastMousePosition; + std::optional _mouseDownPosition; + int _startDraggingLeft = 0; + int _scrollLeft = 0; + int _scrollLeftMax = 0; + bool _dragging = false; + Qt::Orientation _scrollingLock = {}; + + int _selected = -1; + int _pressed = -1; + + base::unique_qptr _menu; + base::has_weak_ptr _menuGuard; + +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index c269dba39..94454a586 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/business/data_shortcut_messages.h" #include "data/components/scheduled_messages.h" #include "data/components/sponsored_messages.h" +#include "data/components/top_peers.h" #include "data/notify/data_notify_settings.h" #include "data/stickers/data_stickers.h" #include "data/data_drafts.h" @@ -429,9 +430,13 @@ not_null History::createItem( } return result; } - return message.match([&](const auto &data) { + const auto result = message.match([&](const auto &data) { return makeMessage(id, data, localFlags); }); + if (result->out() && result->isRegular()) { + session().topPeers().increment(peer, result->date()); + } + return result; } std::vector> History::createItems( diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index d0785b2bd..74b983062 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -28,8 +28,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/file_upload.h" #include "storage/storage_account.h" #include "storage/storage_facade.h" +#include "data/components/recent_peers.h" #include "data/components/scheduled_messages.h" #include "data/components/sponsored_messages.h" +#include "data/components/top_peers.h" #include "data/data_session.h" #include "data/data_changes.h" #include "data/data_user.h" @@ -100,6 +102,7 @@ Session::Session( , _attachWebView(std::make_unique(this)) , _scheduledMessages(std::make_unique(this)) , _sponsoredMessages(std::make_unique(this)) +, _topPeers(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 4249f888f..717df1751 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -31,8 +31,10 @@ class Templates; namespace Data { class Session; class Changes; +class RecentPeers; class ScheduledMessages; class SponsoredMessages; +class TopPeers; } // namespace Data namespace Storage { @@ -106,12 +108,18 @@ public: [[nodiscard]] Data::Changes &changes() const { return *_changes; } + [[nodiscard]] Data::RecentPeers &recentPeers() const { + return *_recentPeers; + } [[nodiscard]] Data::SponsoredMessages &sponsoredMessages() const { return *_sponsoredMessages; } [[nodiscard]] Data::ScheduledMessages &scheduledMessages() const { return *_scheduledMessages; } + [[nodiscard]] Data::TopPeers &topPeers() const { + return *_topPeers; + } [[nodiscard]] Api::Updates &updates() const { return *_updates; } @@ -232,8 +240,10 @@ private: const std::unique_ptr _giftBoxStickersPacks; const std::unique_ptr _sendAsPeers; const std::unique_ptr _attachWebView; + const std::unique_ptr _recentPeers; const std::unique_ptr _scheduledMessages; const std::unique_ptr _sponsoredMessages; + const std::unique_ptr _topPeers; const std::unique_ptr _supportHelper; diff --git a/Telegram/SourceFiles/mtproto/mtproto_config.cpp b/Telegram/SourceFiles/mtproto/mtproto_config.cpp index 4986f7190..454339f49 100644 --- a/Telegram/SourceFiles/mtproto/mtproto_config.cpp +++ b/Telegram/SourceFiles/mtproto/mtproto_config.cpp @@ -45,7 +45,8 @@ QByteArray Config::serialize() const { + Serialize::stringSize(_fields.txtDomainString) + 3 * sizeof(qint32) + Serialize::stringSize(_fields.reactionDefaultEmoji) - + sizeof(quint64); + + sizeof(quint64) + + sizeof(qint32); auto result = QByteArray(); result.reserve(size); @@ -89,7 +90,8 @@ QByteArray Config::serialize() const { << qint32(_fields.blockedMode ? 1 : 0) << qint32(_fields.captionLengthMax) << _fields.reactionDefaultEmoji - << quint64(_fields.reactionDefaultCustom); + << quint64(_fields.reactionDefaultCustom) + << qint32(_fields.ratingDecay); } return result; } @@ -185,6 +187,9 @@ std::unique_ptr Config::FromSerialized(const QByteArray &serialized) { read(raw->_fields.reactionDefaultEmoji); read(raw->_fields.reactionDefaultCustom); } + if (!stream.atEnd()) { + read(raw->_fields.ratingDecay); + } if (stream.status() != QDataStream::Ok || !raw->_dcOptions.constructFromSerialized(dcOptionsSerialized)) { @@ -249,6 +254,10 @@ void Config::apply(const MTPDconfig &data) { }); } _fields.autologinToken = qs(data.vautologin_token().value_or_empty()); + _fields.ratingDecay = data.vrating_e_decay().v; + if (_fields.ratingDecay <= 0) { + _fields.ratingDecay = ConfigFields().ratingDecay; + } if (data.vdc_options().v.empty()) { LOG(("MTP Error: config with empty dc_options received!")); diff --git a/Telegram/SourceFiles/mtproto/mtproto_config.h b/Telegram/SourceFiles/mtproto/mtproto_config.h index fc2ed1c17..8a4db80df 100644 --- a/Telegram/SourceFiles/mtproto/mtproto_config.h +++ b/Telegram/SourceFiles/mtproto/mtproto_config.h @@ -39,6 +39,7 @@ struct ConfigFields { QString txtDomainString; bool blockedMode = false; int captionLengthMax = 1024; + int ratingDecay = 2419200; QString reactionDefaultEmoji = ConfigDefaultReactionEmoji(); uint64 reactionDefaultCustom; QString autologinToken; diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 65bbc1eaa..5c0d6e173 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -87,6 +87,8 @@ PRIVATE dialogs/dialogs_three_state_icon.h dialogs/ui/dialogs_stories_list.cpp dialogs/ui/dialogs_stories_list.h + dialogs/ui/top_peers_strip.cpp + dialogs/ui/top_peers_strip.h editor/controllers/undo_controller.cpp editor/controllers/undo_controller.h From b259c566b7ead259624cbc915c12582bbb8d4e0a Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Apr 2024 11:49:18 +0400 Subject: [PATCH 017/175] Top peer unread badges and online indicators. --- Telegram/Resources/langs/lang.strings | 8 + Telegram/SourceFiles/dialogs/dialogs.style | 10 + .../dialogs/ui/dialogs_suggestions.cpp | 43 +++- .../dialogs/ui/top_peers_strip.cpp | 191 ++++++++++++++++-- .../SourceFiles/dialogs/ui/top_peers_strip.h | 5 +- 5 files changed, 229 insertions(+), 28 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f3be3dc3d..f6c4e47a5 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -5087,6 +5087,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_limit_upload_increase_speed" = "by **{percent}**"; "lng_limit_upload_subscribe_link" = "Telegram Premium"; +"lng_recent_title" = "Recent"; +"lng_recent_clear" = "Clear"; +"lng_recent_clear_sure" = "Do you want to clear your search history?"; +"lng_recent_remove" = "Remove from Recent"; +"lng_recent_hide_top" = "Hide all"; +"lng_recent_hide_sure" = "Are you sure you want to hide frequent contacts list?\n\nYou can always return this list in Settings > Privacy > Suggest Frequent Contacts."; +"lng_recent_hide_button" = "Hide"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index d0d340b09..8d219e71c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -595,6 +595,7 @@ topPeers: DialogsStories(dialogsStoriesFull) { photo: 46px; photoLeft: 10px; photoTop: 8px; + nameLeft: 4px; } dialogsStoriesList: DialogsStoriesList { @@ -628,6 +629,15 @@ dialogsStoriesTooltipHide: IconButton(defaultIconButton) { searchedBarHeight: 32px; searchedBarFont: normalFont; searchedBarPosition: point(17px, 7px); +searchedBarLabel: FlatLabel(defaultFlatLabel) { + textFg: searchedBarFg; + margin: margins(17px, 7px, 17px, 7px); +} +searchedBarLink: LinkButton(defaultLinkButton) { + color: searchedBarFg; + overColor: searchedBarFg; + padding: margins(17px, 7px, 17px, 7px); +} dialogsSearchTagSkip: point(8px, 4px); dialogsSearchTagBottom: 10px; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index 9c9e90bc4..53ef01cd1 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -9,12 +9,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/components/top_peers.h" #include "data/data_user.h" +#include "lang/lang_keys.h" #include "main/main_session.h" +#include "ui/widgets/buttons.h" #include "ui/widgets/elastic_scroll.h" #include "ui/widgets/labels.h" #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/dynamic_thumbnails.h" +#include "styles/style_dialogs.h" #include "styles/style_layers.h" namespace Dialogs { @@ -51,27 +54,45 @@ void Suggestions::resizeEvent(QResizeEvent *e) { } object_ptr Suggestions::setupDivider() { - auto result = object_ptr( + auto result = object_ptr( this, - object_ptr( - this, - rpl::single(u"Recent"_q), - st::boxDividerLabel), - st::defaultBoxDividerLabelPadding); - + st::searchedBarHeight); + const auto raw = result.data(); + const auto label = Ui::CreateChild( + raw, + tr::lng_recent_title(), + st::searchedBarLabel); + const auto clear = Ui::CreateChild( + raw, + tr::lng_recent_clear(tr::now), + st::searchedBarLink); + rpl::combine( + raw->sizeValue(), + clear->widthValue() + ) | rpl::start_with_next([=](QSize size, int width) { + const auto x = st::searchedBarPosition.x(); + const auto y = st::searchedBarPosition.y(); + clear->moveToRight(0, 0, size.width()); + label->resizeToWidth(size.width() - x - width); + label->moveToLeft(x, y, size.width()); + }, raw->lifetime()); + raw->paintRequest() | rpl::start_with_next([=](QRect clip) { + QPainter(raw).fillRect(clip, st::searchedBarBg); + }, raw->lifetime()); return result; } TopPeersList TopPeersContent(not_null session) { - auto result = TopPeersList(); - for (const auto &peer : session->topPeers().list()) { - result.entries.push_back(TopPeersEntry{ + auto base = TopPeersList(); + const auto top = session->topPeers().list(); + for (const auto &peer : top) { + base.entries.push_back(TopPeersEntry{ .id = peer->id.value, .name = peer->shortName(), .userpic = Ui::MakeUserpicThumbnail(peer), }); } - return result; + return base; } } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index 65adee797..12726cee6 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -7,11 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "dialogs/ui/top_peers_strip.h" +#include "ui/effects/ripple_animation.h" #include "ui/text/text.h" #include "ui/widgets/menu/menu_add_action_callback_factory.h" #include "ui/widgets/popup_menu.h" #include "ui/dynamic_image.h" #include "ui/painter.h" +#include "ui/unread_badge_paint.h" #include "styles/style_dialogs.h" #include "styles/style_widgets.h" @@ -23,7 +25,16 @@ struct TopPeersStrip::Entry { uint64 id = 0; Ui::Text::String name; std::shared_ptr userpic; - bool subscribed = false; + std::unique_ptr ripple; + Ui::Animations::Simple onlineShown; + QImage userpicFrame; + float64 userpicFrameOnline = 0.; + QString badgeString; + uint32 badge : 28 = 0; + uint32 userpicFrameDirty : 1 = 0; + uint32 subscribed : 1 = 0; + uint32 online : 1 = 0; + uint32 muted : 1 = 0; }; TopPeersStrip::TopPeersStrip( @@ -39,7 +50,9 @@ TopPeersStrip::TopPeersStrip( setMouseTracking(true); } -TopPeersStrip::~TopPeersStrip() = default; +TopPeersStrip::~TopPeersStrip() { + unsubscribeUserpics(true); +} void TopPeersStrip::resizeEvent(QResizeEvent *e) { updateScrollMax(); @@ -73,6 +86,7 @@ void TopPeersStrip::wheelEvent(QWheelEvent *e) { const auto next = std::clamp(used, 0, _scrollLeftMax); if (next != now) { _scrollLeft = next; + unsubscribeUserpics(); updateSelected(); update(); } @@ -114,11 +128,45 @@ void TopPeersStrip::checkDragging() { _scrollLeftMax); if (newLeft != _scrollLeft) { _scrollLeft = newLeft; + unsubscribeUserpics(); update(); } } } +void TopPeersStrip::unsubscribeUserpics(bool all) { + const auto &st = st::topPeers; + const auto single = st.photoLeft * 2 + st.photo; + auto x = -_scrollLeft; + for (auto &entry : _entries) { + if (all || x + single <= 0 || x >= width()) { + if (entry.subscribed) { + entry.userpic->subscribeToUpdates(nullptr); + entry.subscribed = false; + } + entry.userpicFrame = QImage(); + entry.onlineShown.stop(); + entry.ripple = nullptr; + } + x += single; + } +} + +void TopPeersStrip::subscribeUserpic(Entry &entry) { + const auto raw = entry.userpic.get(); + entry.userpic->subscribeToUpdates([=] { + const auto i = ranges::find( + _entries, + raw, + [&](const Entry &entry) { return entry.userpic.get(); }); + if (i != end(_entries)) { + i->userpicFrameDirty = 1; + } + update(); + }); + entry.subscribed = true; +} + void TopPeersStrip::mouseReleaseEvent(QMouseEvent *e) { _lastMousePosition = e->globalPos(); const auto guard = gsl::finally([&] { @@ -143,6 +191,7 @@ void TopPeersStrip::updateScrollMax() { const auto widthFull = int(_entries.size()) * single; _scrollLeftMax = std::max(widthFull - width(), 0); _scrollLeft = std::clamp(_scrollLeft, 0, _scrollLeftMax); + unsubscribeUserpics(); update(); } @@ -181,10 +230,11 @@ void TopPeersStrip::apply(const TopPeersList &list) { for (auto &entry : _entries) { if (entry.subscribed) { entry.userpic->subscribeToUpdates(nullptr); - entry.subscribed = 0; + entry.subscribed = false; } } _entries = std::move(now); + unsubscribeUserpics(); if (!_entries.empty()) { _empty = false; } @@ -201,37 +251,146 @@ void TopPeersStrip::apply(Entry &entry, const TopPeersEntry &data) { if (entry.userpic.get() != data.userpic.get()) { if (entry.subscribed) { entry.userpic->subscribeToUpdates(nullptr); - entry.subscribed = 0; } entry.userpic = data.userpic; + if (entry.subscribed) { + subscribeUserpic(entry); + } + } + if (entry.online != data.online) { + entry.online = data.online; + if (!entry.subscribed) { + entry.onlineShown.stop(); + } else { + entry.onlineShown.start( + [=] { update(); }, + entry.online ? 0. : 1., + entry.online ? 1. : 0., + st::dialogsOnlineBadgeDuration); + } + } + if (entry.badge != data.badge) { + entry.badge = data.badge; + entry.badgeString = QString(); + entry.userpicFrameDirty = 1; + } + if (entry.muted != data.muted) { + entry.muted = data.muted; + if (entry.badge) { + entry.userpicFrameDirty = 1; + } } } void TopPeersStrip::paintEvent(QPaintEvent *e) { auto p = Painter(this); - auto x = -_scrollLeft; const auto &st = st::topPeers; const auto line = st.lineTwice / 2; const auto single = st.photoLeft * 2 + st.photo; - for (auto &entry : _entries) { + + const auto from = std::min(_scrollLeft / single, int(_entries.size())); + const auto till = std::max( + (_scrollLeft + width() + single - 1) / single + 1, + from); + + auto x = -_scrollLeft + from * single; + for (auto i = from; i != till; ++i) { + auto &entry = _entries[i]; if (!entry.subscribed) { - entry.userpic->subscribeToUpdates([=] { - update(); - }); - entry.subscribed = 1; + subscribeUserpic(entry); } - const auto image = entry.userpic->image(st.photo); - p.drawImage( - QRect(x + st.photoLeft, st.photoTop, st.photo, st.photo), - image); + paintUserpic(p, i, x); const auto nameLeft = x + st.nameLeft; - entry.name.drawElided(p, nameLeft, st.nameTop, single, 1, style::al_top); - + const auto nameWidth = single - 2 * st.nameLeft; + entry.name.drawElided( + p, + nameLeft, + st.nameTop, + nameWidth, + 1, + style::al_top); x += single; } } +void TopPeersStrip::paintUserpic(Painter &p, int index, int x) { + Expects(index >= 0 && index < _entries.size()); + + auto &entry = _entries[index]; + const auto &st = st::topPeers; + const auto size = st.photo; + const auto rect = QRect(x + st.photoLeft, st.photoTop, size, size); + + const auto online = entry.onlineShown.value(entry.online ? 1. : 0.); + const auto useFrame = !entry.userpicFrame.isNull() + && !entry.userpicFrameDirty + && (entry.userpicFrameOnline == online); + if (useFrame) { + p.drawImage(rect, entry.userpicFrame); + return; + } + const auto simple = entry.userpic->image(size); + const auto ratio = style::DevicePixelRatio(); + const auto renderFrame = (online > 0) || entry.badge; + if (!renderFrame) { + entry.userpicFrame = QImage(); + p.drawImage(rect, simple); + return; + } else if (entry.userpicFrame.size() != QSize(size, size) * ratio) { + entry.userpicFrame = QImage( + QSize(size, size) * ratio, + QImage::Format_ARGB32_Premultiplied); + entry.userpicFrame.setDevicePixelRatio(ratio); + } + entry.userpicFrame.fill(Qt::transparent); + entry.userpicFrameDirty = 0; + entry.userpicFrameOnline = online; + + auto q = QPainter(&entry.userpicFrame); + const auto inner = QRect(0, 0, size, size); + q.drawImage(inner, simple); + + auto hq = PainterHighQualityEnabler(p); + + if (online > 0) { + q.setCompositionMode(QPainter::CompositionMode_Source); + const auto onlineSize = st::dialogsOnlineBadgeSize; + const auto stroke = st::dialogsOnlineBadgeStroke; + const auto skip = st::dialogsOnlineBadgeSkip; + const auto shrink = (onlineSize / 2) * (1. - online); + + auto pen = QPen(Qt::transparent); + pen.setWidthF(stroke * online); + q.setPen(pen); + q.setBrush(st::dialogsOnlineBadgeFg); + q.drawEllipse(QRectF( + size - skip.x() - onlineSize, + size - skip.y() - onlineSize, + onlineSize, + onlineSize + ).marginsRemoved({ shrink, shrink, shrink, shrink })); + q.setCompositionMode(QPainter::CompositionMode_SourceOver); + } + + if (entry.badge) { + if (entry.badgeString.isEmpty()) { + entry.badgeString = (entry.badge < 1000) + ? QString::number(entry.badge) + : (QString::number(entry.badge / 1000) + 'K'); + } + auto st = Ui::UnreadBadgeStyle(); + st.selected = (_selected == index); + st.muted = entry.muted; + const auto &counter = entry.badgeString; + const auto badge = PaintUnreadBadge(q, counter, size, 0, st); + } + + q.end(); + + p.drawImage(rect, entry.userpicFrame); +} + void TopPeersStrip::contextMenuEvent(QContextMenuEvent *e) { _menu = nullptr; diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 551b82480..9875fc4b2 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -21,7 +21,7 @@ struct TopPeersEntry { uint64 id = 0; QString name; std::shared_ptr userpic; - uint32 badge : 30 = 0; + uint32 badge : 28 = 0; uint32 muted : 1 = 0; uint32 online : 1 = 0; }; @@ -63,6 +63,9 @@ private: void updateSelected(); void checkDragging(); bool finishDragging(); + void subscribeUserpic(Entry &entry); + void unsubscribeUserpics(bool all = false); + void paintUserpic(Painter &p, int index, int x); void apply(const TopPeersList &list); void apply(Entry &entry, const TopPeersEntry &data); From 56e28feb0006cd070f437015e09f69f9908fb1d2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Apr 2024 12:41:32 +0400 Subject: [PATCH 018/175] Top peer realtime badges. --- .../SourceFiles/dialogs/dialogs_widget.cpp | 2 +- .../dialogs/ui/dialogs_suggestions.cpp | 156 ++++++++++++++++-- .../dialogs/ui/dialogs_suggestions.h | 2 +- .../dialogs/ui/top_peers_strip.cpp | 2 +- .../SourceFiles/dialogs/ui/top_peers_strip.h | 1 + .../SourceFiles/ui/dynamic_thumbnails.cpp | 61 ++++++- Telegram/SourceFiles/ui/dynamic_thumbnails.h | 1 + 7 files changed, 205 insertions(+), 20 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index f60781b44..82cec9097 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -1108,7 +1108,7 @@ void Widget::updateSuggestions(anim::type animated) { } else if (suggest && !_suggestions) { _suggestions = std::make_unique( this, - rpl::single(TopPeersContent(&session()))); + TopPeersContent(&session())); _suggestions->topPeerChosen( ) | rpl::start_with_next([=](PeerId id) { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index 53ef01cd1..7e312c602 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -7,8 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "dialogs/ui/dialogs_suggestions.h" +#include "base/unixtime.h" #include "data/components/top_peers.h" +#include "data/data_changes.h" +#include "data/data_peer_values.h" +#include "data/data_session.h" #include "data/data_user.h" +#include "history/history.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/widgets/buttons.h" @@ -82,17 +87,146 @@ object_ptr Suggestions::setupDivider() { return result; } -TopPeersList TopPeersContent(not_null session) { - auto base = TopPeersList(); - const auto top = session->topPeers().list(); - for (const auto &peer : top) { - base.entries.push_back(TopPeersEntry{ - .id = peer->id.value, - .name = peer->shortName(), - .userpic = Ui::MakeUserpicThumbnail(peer), - }); - } - return base; +rpl::producer TopPeersContent( + not_null session) { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + struct Entry { + not_null history; + int index = 0; + }; + struct State { + TopPeersList data; + base::flat_map, Entry> indices; + base::has_weak_ptr guard; + bool scheduled = true; + }; + auto state = lifetime.make_state(); + const auto top = session->topPeers().list(); + auto &entries = state->data.entries; + auto &indices = state->indices; + entries.reserve(top.size()); + indices.reserve(top.size()); + const auto now = base::unixtime::now(); + for (const auto &peer : top) { + const auto user = peer->asUser(); + const auto self = user && user->isSelf(); + const auto history = peer->owner().history(peer); + const auto badges = history->chatListBadgesState(); + entries.push_back({ + .id = peer->id.value, + .name = (self + ? tr::lng_saved_messages(tr::now) + : peer->shortName()), + .userpic = (self + ? Ui::MakeSavedMessagesThumbnail() + : Ui::MakeUserpicThumbnail(peer)), + .badge = uint32(badges.unreadCounter), + .unread = badges.unread, + .muted = !self && history->muted(), + .online = user && !self && Data::IsUserOnline(user, now), + }); + if (entries.back().online) { + user->owner().watchForOffline(user, now); + } + indices.emplace(peer, Entry{ + .history = peer->owner().history(peer), + .index = int(entries.size()) - 1, + }); + } + + const auto push = [=] { + if (!state->scheduled) { + return; + } + state->scheduled = false; + consumer.put_next_copy(state->data); + }; + const auto schedule = [=] { + if (state->scheduled) { + return; + } + state->scheduled = true; + crl::on_main(&state->guard, push); + }; + + using Flag = Data::PeerUpdate::Flag; + session->changes().peerUpdates( + Flag::Name + | Flag::Photo + | Flag::Notifications + | Flag::OnlineStatus + ) | rpl::start_with_next([=](const Data::PeerUpdate &update) { + const auto peer = update.peer; + if (peer->isSelf()) { + return; + } + const auto i = state->indices.find(peer); + if (i == end(state->indices)) { + return; + } + auto changed = false; + auto &entry = state->data.entries[i->second.index]; + const auto flags = update.flags; + if (flags & Flag::Name) { + const auto now = peer->shortName(); + if (entry.name != now) { + entry.name = now; + changed = true; + } + } + if (flags & Flag::Photo) { + entry.userpic = Ui::MakeUserpicThumbnail(peer); + changed = true; + } + if (flags & Flag::Notifications) { + const auto now = i->second.history->muted(); + if (entry.muted != now) { + entry.muted = now; + changed = true; + } + } + if (flags & Flag::OnlineStatus) { + if (const auto user = peer->asUser()) { + const auto now = base::unixtime::now(); + const auto value = Data::IsUserOnline(user, now); + if (entry.online != value) { + entry.online = value; + changed = true; + if (value) { + user->owner().watchForOffline(user, now); + } + } + } + } + if (changed) { + schedule(); + } + }, lifetime); + + session->data().unreadBadgeChanges( + ) | rpl::start_with_next([=] { + auto changed = false; + auto &entries = state->data.entries; + for (const auto &[peer, data] : state->indices) { + const auto badges = data.history->chatListBadgesState(); + auto &entry = entries[data.index]; + if (entry.badge != badges.unreadCounter + || entry.unread != badges.unread) { + entry.badge = badges.unreadCounter; + entry.unread = badges.unread; + changed = true; + } + } + if (changed) { + schedule(); + } + }, lifetime); + + push(); + return lifetime; + }; } } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h index 10622576a..ada2d4be0 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h @@ -53,7 +53,7 @@ private: }; -[[nodiscard]] TopPeersList TopPeersContent( +[[nodiscard]] rpl::producer TopPeersContent( not_null session); } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index 12726cee6..02ace8a02 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -445,7 +445,7 @@ void TopPeersStrip::updateSelected() { const auto p = mapFromGlobal(_lastMousePosition); const auto x = p.x(); const auto single = st.photoLeft * 2 + st.photo; - const auto index = (x - _scrollLeft) / single; + const auto index = (_scrollLeft + x) / single; const auto selected = (index < 0 || index >= _entries.size()) ? -1 : index; diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 9875fc4b2..1662aa88b 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -22,6 +22,7 @@ struct TopPeersEntry { QString name; std::shared_ptr userpic; uint32 badge : 28 = 0; + uint32 unread : 1 = 0; uint32 muted : 1 = 0; uint32 online : 1 = 0; }; diff --git a/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp b/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp index 847e7d5de..fb977e6d7 100644 --- a/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp +++ b/Telegram/SourceFiles/ui/dynamic_thumbnails.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_photo_media.h" #include "data/data_story.h" #include "main/main_session.h" +#include "ui/empty_userpic.h" #include "ui/dynamic_image.h" #include "ui/painter.h" #include "ui/userpic_view.h" @@ -39,6 +40,7 @@ private: Ui::PeerUserpicView view; Fn callback; InMemoryKey key; + int paletteVersion = 0; rpl::lifetime photoLifetime; rpl::lifetime downloadLifetime; }; @@ -117,6 +119,17 @@ private: }; +class SavedMessagesUserpic final : public DynamicImage { +public: + QImage image(int size) override; + void subscribeToUpdates(Fn callback) override; + +private: + QImage _frame; + int _paletteVersion = 0; + +}; + PeerUserpic::PeerUserpic(not_null peer, bool forceRound) : _peer(peer) , _forceRound(forceRound) { @@ -127,13 +140,21 @@ QImage PeerUserpic::image(int size) { const auto good = (_frame.width() == size * _frame.devicePixelRatio()); const auto key = _peer->userpicUniqueKey(_subscribed->view); - if (!good || (_subscribed->key != key && !waitingUserpicLoad())) { - const auto ratio = style::DevicePixelRatio(); + const auto paletteVersion = style::PaletteVersion(); + if (!good + || (_subscribed->paletteVersion != paletteVersion + && _peer->useEmptyUserpic(_subscribed->view)) + || (_subscribed->key != key && !waitingUserpicLoad())) { _subscribed->key = key; - _frame = QImage( - QSize(size, size) * ratio, - QImage::Format_ARGB32_Premultiplied); - _frame.setDevicePixelRatio(ratio); + _subscribed->paletteVersion = paletteVersion; + + const auto ratio = style::DevicePixelRatio(); + if (!good) { + _frame = QImage( + QSize(size, size) * ratio, + QImage::Format_ARGB32_Premultiplied); + _frame.setDevicePixelRatio(ratio); + } _frame.fill(Qt::transparent); auto p = Painter(&_frame); @@ -313,6 +334,30 @@ QImage EmptyThumbnail::image(int size) { void EmptyThumbnail::subscribeToUpdates(Fn callback) { } +QImage SavedMessagesUserpic::image(int size) { + const auto good = (_frame.width() == size * _frame.devicePixelRatio()); + const auto paletteVersion = style::PaletteVersion(); + if (!good || _paletteVersion != paletteVersion) { + _paletteVersion = paletteVersion; + + const auto ratio = style::DevicePixelRatio(); + if (!good) { + _frame = QImage( + QSize(size, size) * ratio, + QImage::Format_ARGB32_Premultiplied); + _frame.setDevicePixelRatio(ratio); + } + _frame.fill(Qt::transparent); + + auto p = Painter(&_frame); + Ui::EmptyUserpic::PaintSavedMessages(p, 0, 0, size, size); + } + return _frame; +} + +void SavedMessagesUserpic::subscribeToUpdates(Fn callback) { +} + } // namespace std::shared_ptr MakeUserpicThumbnail( @@ -321,6 +366,10 @@ std::shared_ptr MakeUserpicThumbnail( return std::make_shared(peer, forceRound); } +std::shared_ptr MakeSavedMessagesThumbnail() { + return std::make_shared(); +} + std::shared_ptr MakeStoryThumbnail( not_null story) { using Result = std::shared_ptr; diff --git a/Telegram/SourceFiles/ui/dynamic_thumbnails.h b/Telegram/SourceFiles/ui/dynamic_thumbnails.h index cbf3cb235..ad8556d5d 100644 --- a/Telegram/SourceFiles/ui/dynamic_thumbnails.h +++ b/Telegram/SourceFiles/ui/dynamic_thumbnails.h @@ -20,6 +20,7 @@ class DynamicImage; [[nodiscard]] std::shared_ptr MakeUserpicThumbnail( not_null peer, bool forceRound = false); +[[nodiscard]] std::shared_ptr MakeSavedMessagesThumbnail(); [[nodiscard]] std::shared_ptr MakeStoryThumbnail( not_null story); From 19ae76d8de4a492a7cacb71ebca174a09006a849 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Apr 2024 13:12:57 +0400 Subject: [PATCH 019/175] Top peers context menu. --- .../SourceFiles/data/components/top_peers.cpp | 13 +++++ .../SourceFiles/data/components/top_peers.h | 1 + .../SourceFiles/dialogs/dialogs_widget.cpp | 35 +++++++----- Telegram/SourceFiles/dialogs/dialogs_widget.h | 2 +- .../dialogs/ui/dialogs_suggestions.cpp | 54 +++++++++++++++++++ .../dialogs/ui/dialogs_suggestions.h | 5 ++ .../dialogs/ui/top_peers_strip.cpp | 31 +++++++++-- .../SourceFiles/dialogs/ui/top_peers_strip.h | 3 ++ 8 files changed, 126 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/data/components/top_peers.cpp b/Telegram/SourceFiles/data/components/top_peers.cpp index 253c716f3..7d41f2399 100644 --- a/Telegram/SourceFiles/data/components/top_peers.cpp +++ b/Telegram/SourceFiles/data/components/top_peers.cpp @@ -56,6 +56,19 @@ rpl::producer<> TopPeers::updates() const { return _updates.events(); } +void TopPeers::remove(not_null peer) { + const auto i = ranges::find(_list, peer, &TopPeer::peer); + if (i != end(_list)) { + _list.erase(i); + _updates.fire({}); + } + + _requestId = _session->api().request(MTPcontacts_ResetTopPeerRating( + MTP_topPeerCategoryCorrespondents(), + peer->input + )).send(); +} + void TopPeers::increment(not_null peer, TimeId date) { if (date <= _lastReceivedDate) { return; diff --git a/Telegram/SourceFiles/data/components/top_peers.h b/Telegram/SourceFiles/data/components/top_peers.h index b5107381c..67e69261a 100644 --- a/Telegram/SourceFiles/data/components/top_peers.h +++ b/Telegram/SourceFiles/data/components/top_peers.h @@ -22,6 +22,7 @@ public: [[nodiscard]] bool disabled() const; [[nodiscard]] rpl::producer<> updates() const; + void remove(not_null peer); void increment(not_null peer, TimeId date); void reload(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 82cec9097..231bba6b1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -1027,7 +1027,7 @@ void Widget::updateControlsVisibility(bool fast) { _suggestions->show(); } updateStoriesVisibility(); - if ((_openedFolder || _openedForum) && _searchHasFocus.current()) { + if ((_openedFolder || _openedForum) && _searchHasFocus) { setInnerFocus(); } if (_updateTelegram) { @@ -1066,7 +1066,7 @@ void Widget::updateControlsVisibility(bool fast) { if (_hideChildListCanvas) { _hideChildListCanvas->show(); } - if (_childList && _searchHasFocus.current()) { + if (_childList && _searchHasFocus) { setInnerFocus(); } updateLockUnlockPosition(); @@ -1090,16 +1090,26 @@ void Widget::updateLockUnlockPosition() { void Widget::updateHasFocus(not_null focused) { const auto has = (focused == _search.data()); - if (_searchHasFocus.current() != has) { - _searchHasFocus = (focused == _search.data()); - updateStoriesVisibility(); - updateForceDisplayWide(); - updateSuggestions(anim::type::normal); + if (_searchHasFocus != has) { + _searchHasFocus = has; + const auto update = [=] { + updateStoriesVisibility(); + updateForceDisplayWide(); + updateSuggestions(anim::type::normal); + }; + if (has) { + update(); + } else { + // Search field may loose focus from the destructor of some + // widget, in that case we don't want to destroy _suggestions + // syncrhonously, because it may lead to a crash. + crl::on_main(this, update); + } } } void Widget::updateSuggestions(anim::type animated) { - const auto suggest = _searchHasFocus.current() + const auto suggest = _searchHasFocus && !_searchInChat && (_inner->state() == WidgetState::Default); if (!suggest && _suggestions) { @@ -1108,6 +1118,7 @@ void Widget::updateSuggestions(anim::type animated) { } else if (suggest && !_suggestions) { _suggestions = std::make_unique( this, + controller(), TopPeersContent(&session())); _suggestions->topPeerChosen( @@ -1563,7 +1574,7 @@ void Widget::updateStoriesVisibility() { || _openedForum || !_widthAnimationCache.isNull() || _childList - || _searchHasFocus.current() + || _searchHasFocus || !_search->getLastText().isEmpty() || _searchInChat || _stories->empty(); @@ -1681,7 +1692,7 @@ void Widget::slideFinished() { _shownProgressValue = 1.; updateControlsVisibility(true); if ((!_subsectionTopBar || !_subsectionTopBar->searchHasFocus()) - && !_searchHasFocus.current()) { + && !_searchHasFocus) { controller()->widget()->setInnerFocus(); } } @@ -2524,7 +2535,7 @@ void Widget::applySearchUpdate(bool force) { } void Widget::updateForceDisplayWide() { - controller()->setChatsForceDisplayWide(_searchHasFocus.current() + controller()->setChatsForceDisplayWide(_searchHasFocus || !_search->getLastText().isEmpty() || _searchInChat); } @@ -3258,7 +3269,7 @@ bool Widget::cancelSearch() { _inner->clearFilter(); clearSearchField(); applySearchUpdate(); - if (!_searchInChat && _searchHasFocus.current()) { + if (!_searchInChat && _searchHasFocus) { setFocus(); } return clearingQuery || clearingInChat; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index f3b54aa49..6ebd6ab50 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -300,7 +300,7 @@ private: std::vector _searchTags; rpl::lifetime _searchTagsLifetime; QString _lastSearchText; - rpl::variable _searchHasFocus = false; + bool _searchHasFocus = false; rpl::event_stream> _storiesContents; base::flat_map _storiesUserpicsViewsHidden; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index 7e312c602..62e63d554 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -22,13 +22,55 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" #include "ui/dynamic_thumbnails.h" +#include "window/window_session_controller.h" #include "styles/style_dialogs.h" #include "styles/style_layers.h" +#include "styles/style_menu_icons.h" namespace Dialogs { +namespace { + +void FillTopPeerMenu( + not_null controller, + const ShowTopPeerMenuRequest &request, + Fn)> remove) { + const auto owner = &controller->session().data(); + const auto peer = owner->peer(PeerId(request.id)); + const auto &add = request.callback; + const auto group = peer->isMegagroup(); + const auto channel = peer->isChannel(); + + const auto showHistoryText = group + ? tr::lng_context_open_group(tr::now) + : channel + ? tr::lng_context_open_channel(tr::now) + : tr::lng_profile_send_message(tr::now); + add(showHistoryText, [=] { + controller->showPeerHistory(peer); + }, channel ? &st::menuIconChannel : &st::menuIconChatBubble); + + const auto viewProfileText = group + ? tr::lng_context_view_group(tr::now) + : channel + ? tr::lng_context_view_channel(tr::now) + : tr::lng_context_view_profile(tr::now); + add(viewProfileText, [=] { + controller->showPeerInfo(peer); + }, channel ? &st::menuIconInfo : &st::menuIconProfile); + + add({ + .text = tr::lng_recent_remove(tr::now), + .handler = [=] { remove(peer); }, + .icon = &st::menuIconDeleteAttention, + .isAttention = true, + }); +} + +} // namespace Suggestions::Suggestions( not_null parent, + not_null controller, rpl::producer topPeers) : RpWidget(parent) , _scroll(std::make_unique(this)) @@ -45,6 +87,15 @@ Suggestions::Suggestions( _topPeers->clicks() | rpl::start_with_next([=](uint64 peerIdRaw) { _topPeerChosen.fire(PeerId(peerIdRaw)); }, _topPeers->lifetime()); + + _topPeers->showMenuRequests( + ) | rpl::start_with_next([=](const ShowTopPeerMenuRequest &request) { + const auto remove = crl::guard(this, [=](not_null peer) { + peer->session().topPeers().remove(peer); + _topPeers->removeLocally(peer->id.value); + }); + FillTopPeerMenu(controller, request, remove); + }, _topPeers->lifetime()); } Suggestions::~Suggestions() = default; @@ -111,6 +162,9 @@ rpl::producer TopPeersContent( const auto now = base::unixtime::now(); for (const auto &peer : top) { const auto user = peer->asUser(); + if (user->isInaccessible()) { + continue; + } const auto self = user && user->isSelf(); const auto history = peer->owner().history(peer); const auto badges = history->chatListBadgesState(); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h index ada2d4be0..f40455f17 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h @@ -22,12 +22,17 @@ template class SlideWrap; } // namespace Ui +namespace Window { +class SessionController; +} // namespace Window + namespace Dialogs { class Suggestions final : public Ui::RpWidget { public: Suggestions( not_null parent, + not_null controller, rpl::producer topPeers); ~Suggestions(); diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index 02ace8a02..03e7cd09e 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -212,13 +212,29 @@ auto TopPeersStrip::showMenuRequests() const return _showMenuRequests.events(); } +void TopPeersStrip::removeLocally(uint64 id) { + _removed.emplace(id); + const auto i = ranges::find(_entries, id, &Entry::id); + if (i == end(_entries)) { + return; + } else if (i->subscribed) { + i->userpic->subscribeToUpdates(nullptr); + } + _entries.erase(i); + updateScrollMax(); + if (_entries.empty()) { + _empty = true; + } + update(); +} + void TopPeersStrip::apply(const TopPeersList &list) { auto now = std::vector(); - if (list.entries.empty()) { - _empty = true; - } for (const auto &entry : list.entries) { + if (_removed.contains(entry.id)) { + continue; + } const auto i = ranges::find(_entries, entry.id, &Entry::id); if (i != end(_entries)) { now.push_back(base::take(*i)); @@ -227,6 +243,9 @@ void TopPeersStrip::apply(const TopPeersList &list) { } apply(now.back(), entry); } + if (now.empty()) { + _empty = true; + } for (auto &entry : _entries) { if (entry.subscribed) { entry.userpic->subscribeToUpdates(nullptr); @@ -234,6 +253,7 @@ void TopPeersStrip::apply(const TopPeersList &list) { } } _entries = std::move(now); + updateScrollMax(); unsubscribeUserpics(); if (!_entries.empty()) { _empty = false; @@ -289,9 +309,10 @@ void TopPeersStrip::paintEvent(QPaintEvent *e) { const auto single = st.photoLeft * 2 + st.photo; const auto from = std::min(_scrollLeft / single, int(_entries.size())); - const auto till = std::max( + const auto till = std::clamp( (_scrollLeft + width() + single - 1) / single + 1, - from); + from, + int(_entries.size())); auto x = -_scrollLeft + from * single; for (auto i = from; i != till; ++i) { diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 1662aa88b..5eac0ae03 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -49,6 +49,8 @@ public: [[nodiscard]] auto showMenuRequests() const -> rpl::producer; + void removeLocally(uint64 id); + private: struct Entry; @@ -73,6 +75,7 @@ private: std::vector _entries; rpl::variable _empty = true; + base::flat_set _removed; rpl::event_stream _clicks; rpl::event_stream _showMenuRequests; From a6c1def6fe630f7b3ee836f5e7a6b3e4085dbb65 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Apr 2024 13:36:41 +0400 Subject: [PATCH 020/175] Add ripples to top peers. --- Telegram/SourceFiles/dialogs/dialogs.style | 4 +- .../dialogs/ui/top_peers_strip.cpp | 94 +++++++++++++++++-- .../SourceFiles/dialogs/ui/top_peers_strip.h | 6 +- 3 files changed, 93 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 8d219e71c..3f4fffe0a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -595,8 +595,10 @@ topPeers: DialogsStories(dialogsStoriesFull) { photo: 46px; photoLeft: 10px; photoTop: 8px; - nameLeft: 4px; + nameLeft: 6px; } +topPeersRadius: 4px; +topPeersMargin: margins(3px, 3px, 3px, 4px); dialogsStoriesList: DialogsStoriesList { small: dialogsStories; diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index 03e7cd09e..2c8e53d36 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -40,7 +40,8 @@ struct TopPeersStrip::Entry { TopPeersStrip::TopPeersStrip( not_null parent, rpl::producer content) -: RpWidget(parent) { +: RpWidget(parent) +, _selection(st::topPeersRadius, st::windowBgOver) { resize(0, st::topPeers.height); std::move(content) | rpl::start_with_next([=](const TopPeersList &list) { @@ -102,6 +103,23 @@ void TopPeersStrip::mousePressEvent(QMouseEvent *e) { _mouseDownPosition = _lastMousePosition; _pressed = _selected; + + if (_selected >= 0) { + Assert(_selected < _entries.size()); + auto &entry = _entries[_selected]; + if (!entry.ripple) { + entry.ripple = std::make_unique( + st::defaultRippleAnimation, + Ui::RippleAnimation::RoundRectMask( + innerRounded().size(), + st::topPeersRadius), + [=] { update(); }); + } + const auto single = outer().width(); + entry.ripple->add(e->pos() - QPoint( + _selected * single - _scrollLeft + st::topPeersMargin.left(), + st::topPeersMargin.top())); + } } void TopPeersStrip::mouseMoveEvent(QMouseEvent *e) { @@ -174,14 +192,20 @@ void TopPeersStrip::mouseReleaseEvent(QMouseEvent *e) { }); const auto pressed = std::exchange(_pressed, -1); + if (pressed >= 0) { + Assert(pressed < _entries.size()); + auto &entry = _entries[pressed]; + if (entry.ripple) { + entry.ripple->lastStop(); + } + } if (finishDragging()) { return; } updateSelected(); - if (_selected == pressed) { - if (_selected < _entries.size()) { - _clicks.fire_copy(_entries[_selected].id); - } + if (_selected >= 0 && _selected == pressed) { + Assert(_selected < _entries.size()); + _clicks.fire_copy(_entries[_selected].id); } } @@ -220,7 +244,14 @@ void TopPeersStrip::removeLocally(uint64 id) { } else if (i->subscribed) { i->userpic->subscribeToUpdates(nullptr); } + const auto index = int(i - begin(_entries)); _entries.erase(i); + if (_selected > index) { + --_selected; + } + if (_pressed > index) { + --_pressed; + } updateScrollMax(); if (_entries.empty()) { _empty = true; @@ -231,6 +262,8 @@ void TopPeersStrip::removeLocally(uint64 id) { void TopPeersStrip::apply(const TopPeersList &list) { auto now = std::vector(); + auto selectedId = (_selected >= 0) ? _entries[_selected].id : 0; + auto pressedId = (_pressed >= 0) ? _entries[_pressed].id : 0; for (const auto &entry : list.entries) { if (_removed.contains(entry.id)) { continue; @@ -253,6 +286,18 @@ void TopPeersStrip::apply(const TopPeersList &list) { } } _entries = std::move(now); + if (selectedId) { + const auto i = ranges::find(_entries, selectedId, &Entry::id); + if (i != end(_entries)) { + _selected = int(i - begin(_entries)); + } + } + if (pressedId) { + const auto i = ranges::find(_entries, pressedId, &Entry::id); + if (i != end(_entries)) { + _pressed = int(i - begin(_entries)); + } + } updateScrollMax(); unsubscribeUserpics(); if (!_entries.empty()) { @@ -302,10 +347,19 @@ void TopPeersStrip::apply(Entry &entry, const TopPeersEntry &data) { } } +QRect TopPeersStrip::outer() const { + const auto &st = st::topPeers; + const auto single = st.photoLeft * 2 + st.photo; + return QRect(0, 0, single, height()); +} + +QRect TopPeersStrip::innerRounded() const { + return outer().marginsRemoved(st::topPeersMargin); +} + void TopPeersStrip::paintEvent(QPaintEvent *e) { auto p = Painter(this); const auto &st = st::topPeers; - const auto line = st.lineTwice / 2; const auto single = st.photoLeft * 2 + st.photo; const auto from = std::min(_scrollLeft / single, int(_entries.size())); @@ -315,12 +369,28 @@ void TopPeersStrip::paintEvent(QPaintEvent *e) { int(_entries.size())); auto x = -_scrollLeft + from * single; + const auto highlighted = (_pressed >= 0) ? _pressed : _selected; for (auto i = from; i != till; ++i) { auto &entry = _entries[i]; + const auto selected = (i == highlighted); + if (selected) { + _selection.paint(p, innerRounded().translated(x, 0)); + } + if (entry.ripple) { + entry.ripple->paint( + p, + x + st::topPeersMargin.left(), + st::topPeersMargin.top(), + width()); + if (entry.ripple->empty()) { + entry.ripple = nullptr; + } + } + if (!entry.subscribed) { subscribeUserpic(entry); } - paintUserpic(p, i, x); + paintUserpic(p, x, i, selected); const auto nameLeft = x + st.nameLeft; const auto nameWidth = single - 2 * st.nameLeft; @@ -335,7 +405,11 @@ void TopPeersStrip::paintEvent(QPaintEvent *e) { } } -void TopPeersStrip::paintUserpic(Painter &p, int index, int x) { +void TopPeersStrip::paintUserpic( + Painter &p, + int x, + int index, + bool selected) { Expects(index >= 0 && index < _entries.size()); auto &entry = _entries[index]; @@ -401,7 +475,7 @@ void TopPeersStrip::paintUserpic(Painter &p, int index, int x) { : (QString::number(entry.badge / 1000) + 'K'); } auto st = Ui::UnreadBadgeStyle(); - st.selected = (_selected == index); + st.selected = selected; st.muted = entry.muted; const auto &counter = entry.badgeString; const auto badge = PaintUnreadBadge(q, counter, size, 0, st); @@ -422,6 +496,7 @@ void TopPeersStrip::contextMenuEvent(QContextMenuEvent *e) { if (_selected < 0 || _entries.empty()) { return; } + Assert(_selected < _entries.size()); _menu = base::make_unique_q( this, st::popupMenuWithIcons); @@ -476,6 +551,7 @@ void TopPeersStrip::updateSelected() { setCursor(over ? style::cur_pointer : style::cur_default); } _selected = selected; + update(); } } diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 5eac0ae03..0eead5cc5 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" #include "ui/widgets/menu/menu_add_action_callback.h" +#include "ui/round_rect.h" #include "ui/rp_widget.h" namespace Ui { @@ -68,8 +69,10 @@ private: bool finishDragging(); void subscribeUserpic(Entry &entry); void unsubscribeUserpics(bool all = false); - void paintUserpic(Painter &p, int index, int x); + void paintUserpic(Painter &p, int x, int index, bool selected); + [[nodiscard]] QRect outer() const; + [[nodiscard]] QRect innerRounded() const; void apply(const TopPeersList &list); void apply(Entry &entry, const TopPeersEntry &data); @@ -92,6 +95,7 @@ private: int _selected = -1; int _pressed = -1; + Ui::RoundRect _selection; base::unique_qptr _menu; base::has_weak_ptr _menuGuard; From e1c21b908c3da7cc8bb70d404968df59644d0558 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Apr 2024 13:55:38 +0400 Subject: [PATCH 021/175] Add keyboard navigation to top peers. --- .../SourceFiles/dialogs/dialogs_widget.cpp | 32 ++++++- .../dialogs/ui/dialogs_suggestions.cpp | 37 +++++++++ .../dialogs/ui/dialogs_suggestions.h | 6 ++ .../dialogs/ui/top_peers_strip.cpp | 83 ++++++++++++++++++- .../SourceFiles/dialogs/ui/top_peers_strip.h | 11 +++ 5 files changed, 162 insertions(+), 7 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 231bba6b1..98956f32f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -1720,7 +1720,10 @@ void Widget::escape() { } void Widget::submit() { - if (_inner->chooseRow()) { + if (_suggestions) { + _suggestions->chooseRow(); + return; + } else if (_inner->chooseRow()) { return; } const auto state = _inner->state(); @@ -3142,13 +3145,34 @@ void Widget::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { submit(); } else if (e->key() == Qt::Key_Down) { - _inner->selectSkip(1); + if (_suggestions) { + _suggestions->selectSkip(1); + } else { + _inner->selectSkip(1); + } } else if (e->key() == Qt::Key_Up) { + if (_suggestions) { + _suggestions->selectSkip(-1); + } else { + _inner->selectSkip(-1); + } _inner->selectSkip(-1); + } else if (e->key() == Qt::Key_Left && _suggestions) { + _suggestions->selectLeft(); + } else if (e->key() == Qt::Key_Right && _suggestions) { + _suggestions->selectRight(); } else if (e->key() == Qt::Key_PageDown) { - _inner->selectSkipPage(_scroll->height(), 1); + if (_suggestions) { + _suggestions->selectSkipPage(_scroll->height(), 1); + } else { + _inner->selectSkipPage(_scroll->height(), 1); + } } else if (e->key() == Qt::Key_PageUp) { - _inner->selectSkipPage(_scroll->height(), -1); + if (_suggestions) { + _suggestions->selectSkipPage(_scroll->height(), -1); + } else { + _inner->selectSkipPage(_scroll->height(), -1); + } } else if (!(e->modifiers() & ~Qt::ShiftModifier) && e->key() != Qt::Key_Shift && !_openedFolder diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index 62e63d554..b62c662c4 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -100,6 +100,43 @@ Suggestions::Suggestions( Suggestions::~Suggestions() = default; +void Suggestions::selectSkip(int delta) { + if (!delta) { + return; + } else if (delta > 0) { + const auto hasRecent = false; + if (hasRecent && (_topPeers->selectedByKeyboard() || delta > 1)) { + _topPeers->deselectByKeyboard(); + } else { + _topPeers->selectByKeyboard(0); + } + } else { + if (_topPeers->selectedByKeyboard()) { + _topPeers->deselectByKeyboard(); + } + } +} + +void Suggestions::selectSkipPage(int height, int direction) { + if (_topPeers->selectedByKeyboard()) { + _topPeers->deselectByKeyboard(); + } +} + +void Suggestions::chooseRow() { + if (_topPeers->chooseRow()) { + return; + } +} + +void Suggestions::selectLeft() { + _topPeers->selectLeft(); +} + +void Suggestions::selectRight() { + _topPeers->selectRight(); +} + void Suggestions::paintEvent(QPaintEvent *e) { QPainter(this).fillRect(e->rect(), st::windowBg); } diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h index f40455f17..aa6491f42 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.h @@ -36,6 +36,12 @@ public: rpl::producer topPeers); ~Suggestions(); + void selectSkip(int delta); + void selectSkipPage(int height, int direction); + void selectLeft(); + void selectRight(); + void chooseRow(); + [[nodiscard]] rpl::producer topPeerChosen() const { return _topPeerChosen.events(); } diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index 2c8e53d36..74756f53c 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -94,6 +94,11 @@ void TopPeersStrip::wheelEvent(QWheelEvent *e) { e->accept(); } +void TopPeersStrip::leaveEventHook(QEvent *e) { + setSelected(-1); + _selectionByKeyboard = false; +} + void TopPeersStrip::mousePressEvent(QMouseEvent *e) { if (e->button() != Qt::LeftButton) { return; @@ -123,7 +128,11 @@ void TopPeersStrip::mousePressEvent(QMouseEvent *e) { } void TopPeersStrip::mouseMoveEvent(QMouseEvent *e) { + if (_lastMousePosition == e->globalPos() && _selectionByKeyboard) { + return; + } _lastMousePosition = e->globalPos(); + _selectionByKeyboard = false; updateSelected(); if (!_dragging && _mouseDownPosition) { @@ -259,6 +268,58 @@ void TopPeersStrip::removeLocally(uint64 id) { update(); } +bool TopPeersStrip::selectedByKeyboard() const { + return _selectionByKeyboard && _selected >= 0; +} + +void TopPeersStrip::selectByKeyboard(int delta) { + if (_entries.empty()) { + return; + } + _selectionByKeyboard = true; + if (!delta) { + if (_selected < 0) { + setSelected(0); + scrollToSelected(); + } + return; + } + setSelected(std::clamp(_selected + delta, 0, int(_entries.size()) - 1)); + scrollToSelected(); +} + +void TopPeersStrip::deselectByKeyboard() { + if (_selectionByKeyboard) { + _selectionByKeyboard = false; + setSelected(-1); + } +} + +void TopPeersStrip::selectLeft() { + if (_selected > 0) { + _selectionByKeyboard = true; + setSelected(_selected - 1); + scrollToSelected(); + } +} + +void TopPeersStrip::selectRight() { + if (_selected + 1 < _entries.size()) { + _selectionByKeyboard = true; + setSelected(_selected + 1); + scrollToSelected(); + } +} + +bool TopPeersStrip::chooseRow() { + if (_selected >= 0) { + Assert(_selected < _entries.size()); + _clicks.fire_copy(_entries[_selected].id); + return true; + } + return false; +} + void TopPeersStrip::apply(const TopPeersList &list) { auto now = std::vector(); @@ -542,9 +603,10 @@ void TopPeersStrip::updateSelected() { const auto x = p.x(); const auto single = st.photoLeft * 2 + st.photo; const auto index = (_scrollLeft + x) / single; - const auto selected = (index < 0 || index >= _entries.size()) - ? -1 - : index; + setSelected((index < 0 || index >= _entries.size()) ? -1 : index); +} + +void TopPeersStrip::setSelected(int selected) { if (_selected != selected) { const auto over = (selected >= 0); if (over != (_selected >= 0)) { @@ -555,4 +617,19 @@ void TopPeersStrip::updateSelected() { } } +void TopPeersStrip::scrollToSelected() { + if (_selected < 0) { + return; + } + const auto &st = st::topPeers; + const auto single = st.photoLeft * 2 + st.photo; + const auto left = _selected * single; + const auto right = left + single; + if (_scrollLeft > left) { + _scrollLeft = std::clamp(left, 0, _scrollLeftMax); + } else if (_scrollLeft + width() < right) { + _scrollLeft = std::clamp(right - width(), 0, _scrollLeftMax); + } +} + } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 0eead5cc5..5a4c9c2b1 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -52,6 +52,13 @@ public: void removeLocally(uint64 id); + [[nodiscard]] bool selectedByKeyboard() const; + void selectByKeyboard(int delta); + void selectLeft(); + void selectRight(); + void deselectByKeyboard(); + bool chooseRow(); + private: struct Entry; @@ -62,9 +69,12 @@ private: void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void contextMenuEvent(QContextMenuEvent *e) override; + void leaveEventHook(QEvent *e) override; void updateScrollMax(); void updateSelected(); + void setSelected(int selected); + void scrollToSelected(); void checkDragging(); bool finishDragging(); void subscribeUserpic(Entry &entry); @@ -94,6 +104,7 @@ private: int _selected = -1; int _pressed = -1; + bool _selectionByKeyboard = false; Ui::RoundRect _selection; base::unique_qptr _menu; From c11f4efc5cb611c8a420829aff34969a17908cb2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 11 Apr 2024 16:32:58 +0400 Subject: [PATCH 022/175] Support disabling top peers completely. --- Telegram/Resources/langs/lang.strings | 3 ++ .../SourceFiles/data/components/top_peers.cpp | 23 +++++++++- .../SourceFiles/data/components/top_peers.h | 1 + .../dialogs/ui/dialogs_suggestions.cpp | 44 ++++++++++++++----- .../dialogs/ui/top_peers_strip.cpp | 12 +++++ .../SourceFiles/dialogs/ui/top_peers_strip.h | 3 +- .../admin_log/history_admin_log_item.cpp | 20 +++------ Telegram/SourceFiles/history/history.cpp | 20 ++++++--- Telegram/SourceFiles/history/history.h | 3 +- .../settings/settings_privacy_security.cpp | 31 +++++++++++++ 10 files changed, 125 insertions(+), 35 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index f6c4e47a5..36c2c9393 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -730,6 +730,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_angle_backend_d3d11on12" = "D3D11on12"; "lng_settings_angle_backend_opengl" = "OpenGL"; "lng_settings_angle_backend_disabled" = "Disabled"; +"lng_settings_top_peers_title" = "Frequent contacts"; +"lng_settings_top_peers_suggest" = "Suggest frequent contacts"; +"lng_settings_top_peers_about" = "Display people you message frequently at the top of the search section for quick access."; "lng_settings_sensitive_title" = "Sensitive content"; "lng_settings_sensitive_disable_filtering" = "Disable filtering"; "lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices."; diff --git a/Telegram/SourceFiles/data/components/top_peers.cpp b/Telegram/SourceFiles/data/components/top_peers.cpp index 7d41f2399..8e6149259 100644 --- a/Telegram/SourceFiles/data/components/top_peers.cpp +++ b/Telegram/SourceFiles/data/components/top_peers.cpp @@ -70,7 +70,7 @@ void TopPeers::remove(not_null peer) { } void TopPeers::increment(not_null peer, TimeId date) { - if (date <= _lastReceivedDate) { + if (_disabled || date <= _lastReceivedDate) { return; } if (const auto user = peer->asUser(); user && !user->isBot()) { @@ -107,6 +107,27 @@ void TopPeers::reload() { request(); } +void TopPeers::toggleDisabled(bool disabled) { + if (disabled) { + if (!_disabled || !_list.empty()) { + _disabled = true; + _list.clear(); + _updates.fire({}); + } + } else if (_disabled) { + _disabled = false; + _updates.fire({}); + } + + _session->api().request(MTPcontacts_ToggleTopPeers( + MTP_bool(!disabled) + )).done([=] { + if (!_disabled) { + request(); + } + }).send(); +} + void TopPeers::request() { if (_requestId) { return; diff --git a/Telegram/SourceFiles/data/components/top_peers.h b/Telegram/SourceFiles/data/components/top_peers.h index 67e69261a..da44a0c6c 100644 --- a/Telegram/SourceFiles/data/components/top_peers.h +++ b/Telegram/SourceFiles/data/components/top_peers.h @@ -25,6 +25,7 @@ public: void remove(not_null peer); void increment(not_null peer, TimeId date); void reload(); + void toggleDisabled(bool disabled); private: struct TopPeer { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index b62c662c4..ad5bce8d0 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "lang/lang_keys.h" #include "main/main_session.h" +#include "ui/boxes/confirm_box.h" #include "ui/widgets/buttons.h" #include "ui/widgets/elastic_scroll.h" #include "ui/widgets/labels.h" @@ -23,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/dynamic_thumbnails.h" #include "window/window_session_controller.h" +#include "styles/style_chat.h" #include "styles/style_dialogs.h" #include "styles/style_layers.h" #include "styles/style_menu_icons.h" @@ -33,13 +35,23 @@ namespace { void FillTopPeerMenu( not_null controller, const ShowTopPeerMenuRequest &request, - Fn)> remove) { + Fn)> remove, + Fn hideAll) { const auto owner = &controller->session().data(); const auto peer = owner->peer(PeerId(request.id)); const auto &add = request.callback; const auto group = peer->isMegagroup(); const auto channel = peer->isChannel(); + add({ + .text = tr::lng_recent_remove(tr::now), + .handler = [=] { remove(peer); }, + .icon = &st::menuIconDeleteAttention, + .isAttention = true, + }); + + add({ .separatorSt = &st::expandedMenuSeparator }); + const auto showHistoryText = group ? tr::lng_context_open_group(tr::now) : channel @@ -58,12 +70,14 @@ void FillTopPeerMenu( controller->showPeerInfo(peer); }, channel ? &st::menuIconInfo : &st::menuIconProfile); - add({ - .text = tr::lng_recent_remove(tr::now), - .handler = [=] { remove(peer); }, - .icon = &st::menuIconDeleteAttention, - .isAttention = true, - }); + add({ .separatorSt = &st::expandedMenuSeparator }); + + add(tr::lng_recent_hide_top(tr::now), [=] { + controller->show(Ui::MakeConfirmBox({ + .text = tr::lng_recent_hide_sure(), + .confirmed = [=](Fn close) { hideAll(); close(); } + })); + }, &st::menuIconCancel); } } // namespace @@ -90,11 +104,21 @@ Suggestions::Suggestions( _topPeers->showMenuRequests( ) | rpl::start_with_next([=](const ShowTopPeerMenuRequest &request) { - const auto remove = crl::guard(this, [=](not_null peer) { + const auto weak = Ui::MakeWeak(this); + const auto remove = [=](not_null peer) { peer->session().topPeers().remove(peer); - _topPeers->removeLocally(peer->id.value); + if (weak) { + _topPeers->removeLocally(peer->id.value); + } + }; + const auto session = &controller->session(); + const auto hideAll = crl::guard(session, [=] { + session->topPeers().toggleDisabled(true); + if (weak) { + _topPeers->removeLocally(); + } }); - FillTopPeerMenu(controller, request, remove); + FillTopPeerMenu(controller, request, remove, hideAll); }, _topPeers->lifetime()); } diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp index 74756f53c..01771f9e0 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.cpp @@ -246,6 +246,15 @@ auto TopPeersStrip::showMenuRequests() const } void TopPeersStrip::removeLocally(uint64 id) { + if (!id) { + unsubscribeUserpics(true); + setSelected(-1); + _pressed = -1; + _entries.clear(); + _hiddenLocally = true; + _empty = true; + return; + } _removed.emplace(id); const auto i = ranges::find(_entries, id, &Entry::id); if (i == end(_entries)) { @@ -321,6 +330,9 @@ bool TopPeersStrip::chooseRow() { } void TopPeersStrip::apply(const TopPeersList &list) { + if (_hiddenLocally) { + return; + } auto now = std::vector(); auto selectedId = (_selected >= 0) ? _entries[_selected].id : 0; diff --git a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h index 5a4c9c2b1..84584e56d 100644 --- a/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h +++ b/Telegram/SourceFiles/dialogs/ui/top_peers_strip.h @@ -50,7 +50,7 @@ public: [[nodiscard]] auto showMenuRequests() const -> rpl::producer; - void removeLocally(uint64 id); + void removeLocally(uint64 id = 0); [[nodiscard]] bool selectedByKeyboard() const; void selectByKeyboard(int delta); @@ -105,6 +105,7 @@ private: int _selected = -1; int _pressed = -1; bool _selectionByKeyboard = false; + bool _hiddenLocally = false; Ui::RoundRect _selection; base::unique_qptr _menu; 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 53e0926e0..72d797460 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -953,13 +953,11 @@ void GenerateItems( Ui::Text::WithEntities); addSimpleServiceMessage(text, realId); - const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), PrepareLogMessage(action.vmessage(), date), - MessageFlag::AdminLogEntry, - detachExistingItem), + MessageFlag::AdminLogEntry), ExtractSentDate(action.vmessage()), realId); }, [&](const auto &) { @@ -1008,12 +1006,10 @@ void GenerateItems( Ui::Text::WithEntities); addSimpleServiceMessage(text, realId); - const auto detachExistingItem = false; const auto body = history->createItem( history->nextNonHistoryEntryId(), PrepareLogMessage(action.vnew_message(), date), - MessageFlag::AdminLogEntry, - detachExistingItem); + MessageFlag::AdminLogEntry); if (oldValue.text.isEmpty()) { oldValue = PrepareText( QString(), @@ -1038,13 +1034,11 @@ void GenerateItems( Ui::Text::WithEntities); addSimpleServiceMessage(text, realId); - const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), PrepareLogMessage(action.vmessage(), date), - MessageFlag::AdminLogEntry, - detachExistingItem), + MessageFlag::AdminLogEntry), ExtractSentDate(action.vmessage()), realId); }; @@ -1218,13 +1212,11 @@ void GenerateItems( Ui::Text::WithEntities); addSimpleServiceMessage(text, realId); - const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), PrepareLogMessage(action.vmessage(), date), - MessageFlag::AdminLogEntry, - detachExistingItem), + MessageFlag::AdminLogEntry), ExtractSentDate(action.vmessage()), realId); }; @@ -1604,13 +1596,11 @@ void GenerateItems( Ui::Text::WithEntities); addSimpleServiceMessage(text, realId); - const auto detachExistingItem = false; addPart( history->createItem( history->nextNonHistoryEntryId(), PrepareLogMessage(data.vmessage(), date), - MessageFlag::AdminLogEntry, - detachExistingItem), + MessageFlag::AdminLogEntry), ExtractSentDate(data.vmessage()), realId); }; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 94454a586..dca53737d 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -423,7 +423,8 @@ not_null History::createItem( MsgId id, const MTPMessage &message, MessageFlags localFlags, - bool detachExistingItem) { + bool detachExistingItem, + bool newMessage) { if (const auto result = owner().message(peer, id)) { if (detachExistingItem) { result->removeMainView(); @@ -433,7 +434,7 @@ not_null History::createItem( const auto result = message.match([&](const auto &data) { return makeMessage(id, data, localFlags); }); - if (result->out() && result->isRegular()) { + if (newMessage && result->out() && result->isRegular()) { session().topPeers().increment(peer, result->date()); } return result; @@ -461,16 +462,21 @@ not_null History::addNewMessage( const MTPMessage &message, MessageFlags localFlags, NewMessageType type) { - const auto detachExisting = (type == NewMessageType::Unread); - const auto item = createItem(id, message, localFlags, detachExisting); + const auto newMessage = (type == NewMessageType::Unread); + const auto detachExisting = newMessage; + const auto item = createItem( + id, + message, + localFlags, + detachExisting, + newMessage); if (type == NewMessageType::Existing || item->mainView()) { return item; } - const auto unread = (type == NewMessageType::Unread); - if (unread && item->isHistoryEntry()) { + if (newMessage && item->isHistoryEntry()) { applyMessageChanges(item, message); } - return addNewItem(item, unread); + return addNewItem(item, newMessage); } not_null History::insertItem( diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 4a4ad8ddc..e00c23b8d 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -185,7 +185,8 @@ public: MsgId id, const MTPMessage &message, MessageFlags localFlags, - bool detachExistingItem); + bool detachExistingItem = false, + bool newMessage = false); std::vector> createItems( const QVector &data); diff --git a/Telegram/SourceFiles/settings/settings_privacy_security.cpp b/Telegram/SourceFiles/settings/settings_privacy_security.cpp index 4849719ff..66a26632b 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_security.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_security.cpp @@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_instance.h" #include "core/update_checker.h" #include "lang/lang_keys.h" +#include "data/components/top_peers.h" #include "data/data_session.h" #include "data/data_chat.h" #include "data/data_channel.h" @@ -546,6 +547,35 @@ void SetupCloudPassword( session->api().cloudPassword().reload(); } +void SetupTopPeers( + not_null controller, + not_null container) { + Ui::AddSkip(container); + Ui::AddSubsectionTitle(container, tr::lng_settings_top_peers_title()); + + const auto session = &controller->session(); + + container->add(object_ptr -
- "_q + page.content + R"( -
+
)"_q + page.content.trimmed() + R"(
@@ -646,7 +640,12 @@ void Controller::processLink(const QString &url, const QString &context) { const auto joinPrefix = u"join_link"_q; const auto webpagePrefix = u"webpage"_q; const auto viewerPrefix = u"viewer"_q; - if (context.startsWith(channelPrefix)) { + if (context == u"report-iv") { + _events.fire({ + .type = Event::Type::Report, + .context = QString::number(compuseCurrentPageId()), + }); + } else if (context.startsWith(channelPrefix)) { _events.fire({ .type = Event::Type::OpenChannel, .context = context.mid(channelPrefix.size()), @@ -701,6 +700,13 @@ QString Controller::composeCurrentUrl() const { + (_hash.isEmpty() ? u""_q : ('#' + _hash)); } +uint64 Controller::compuseCurrentPageId() const { + const auto index = _index.current(); + Assert(index >= 0 && index < _pages.size()); + + return _pages[index].pageId; +} + void Controller::showMenu() { const auto index = _index.current(); if (_menu || index < 0 || index > _pages.size()) { diff --git a/Telegram/SourceFiles/iv/iv_controller.h b/Telegram/SourceFiles/iv/iv_controller.h index d3363d67e..473813fca 100644 --- a/Telegram/SourceFiles/iv/iv_controller.h +++ b/Telegram/SourceFiles/iv/iv_controller.h @@ -63,6 +63,7 @@ public: OpenLink, OpenLinkExternal, OpenMedia, + Report, }; Type type = Type::Close; QString url; @@ -116,6 +117,7 @@ private: void quit(); [[nodiscard]] QString composeCurrentUrl() const; + [[nodiscard]] uint64 compuseCurrentPageId() const; void showShareMenu(); void destroyShareMenu(); diff --git a/Telegram/SourceFiles/iv/iv_data.h b/Telegram/SourceFiles/iv/iv_data.h index c33fe1b18..87900bce3 100644 --- a/Telegram/SourceFiles/iv/iv_data.h +++ b/Telegram/SourceFiles/iv/iv_data.h @@ -15,6 +15,7 @@ struct Options { }; struct Prepared { + uint64 pageId = 0; QString name; QByteArray content; QByteArray script; diff --git a/Telegram/SourceFiles/iv/iv_instance.cpp b/Telegram/SourceFiles/iv/iv_instance.cpp index 49ae8c144..babce1d6c 100644 --- a/Telegram/SourceFiles/iv/iv_instance.cpp +++ b/Telegram/SourceFiles/iv/iv_instance.cpp @@ -742,9 +742,7 @@ void Instance::show( not_null data, QString hash) { const auto guard = gsl::finally([&] { - if (data->partial()) { - requestFull(session, data->id()); - } + requestFull(session, data->id()); }); if (_shown && _shownSession == session) { _shown->moveTo(data, hash); @@ -834,6 +832,17 @@ void Instance::show( UrlClickHandler::Open(event.url); }).send(); break; + case Type::Report: + if (const auto controller = _shownSession->tryResolveWindow()) { + controller->window().activate(); + controller->showPeerByLink(Window::PeerByLinkInfo{ + .usernameOrId = "previews", + .resolveType = Window::ResolveType::BotStart, + .startToken = ("webpage" + + QString::number(event.context.toULongLong())), + }); + } + break; } }, _shown->lifetime()); diff --git a/Telegram/SourceFiles/iv/iv_prepare.cpp b/Telegram/SourceFiles/iv/iv_prepare.cpp index f0c823676..699f44677 100644 --- a/Telegram/SourceFiles/iv/iv_prepare.cpp +++ b/Telegram/SourceFiles/iv/iv_prepare.cpp @@ -142,6 +142,8 @@ private: [[nodiscard]] QByteArray block( const MTPDpageListOrderedItemBlocks &data); + [[nodiscard]] QByteArray wrap(const QByteArray &content, int views); + [[nodiscard]] QByteArray tag( const QByteArray &name, const QByteArray &body = {}); @@ -223,9 +225,13 @@ Parser::Parser(const Source &source, const Options &options) : /*_options(options) , */_fileOriginPostfix('/' + Number(source.pageId)) { process(source); + _result.pageId = source.pageId; _result.name = source.name; _result.rtl = source.page.data().is_rtl(); - _result.content = list(source.page.data().vblocks()); + + const auto views = source.page.data().vviews().value_or_empty(); + const auto content = list(source.page.data().vblocks()); + _result.content = wrap(content, views); } Prepared Parser::result() { @@ -925,6 +931,26 @@ QByteArray Parser::utf(const tl::conditional &text) { return text ? utf(*text) : QByteArray(); } +QByteArray Parser::wrap(const QByteArray &content, int views) { + const auto sep = " \xE2\x80\xA2 "; + const auto viewsText = views + ? (tr::lng_stories_views(tr::now, lt_count_decimal, views) + sep) + : QString(); + return R"( +
+
)"_q + content + R"(
+
+
)"_q; +} + QByteArray Parser::tag( const QByteArray &name, const QByteArray &body) { From 2949cdab613aad2ea042605483dd80cf2aece6f0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Apr 2024 19:48:28 +0400 Subject: [PATCH 119/175] Don't request IV two times in a row. --- Telegram/SourceFiles/iv/iv_instance.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Telegram/SourceFiles/iv/iv_instance.cpp b/Telegram/SourceFiles/iv/iv_instance.cpp index babce1d6c..ab2e21327 100644 --- a/Telegram/SourceFiles/iv/iv_instance.cpp +++ b/Telegram/SourceFiles/iv/iv_instance.cpp @@ -813,6 +813,7 @@ void Instance::show( if (!urlChecked) { break; } + _fullRequested[_shownSession].emplace(event.url); _shownSession->api().request(MTPmessages_GetWebPage( MTP_string(event.url), MTP_int(0) @@ -947,6 +948,7 @@ void Instance::openWithIvPreferred( }; _ivRequestSession = session; _ivRequestUri = uri; + _fullRequested[session].emplace(url); _ivRequestId = session->api().request(MTPmessages_GetWebPage( MTP_string(url), MTP_int(0) From 363c191a6e738ee4c33544baa4110e7610cc3cf8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Apr 2024 20:08:44 +0400 Subject: [PATCH 120/175] Skip media bottom skip in IV. --- Telegram/Resources/iv_html/page.css | 3 +++ Telegram/SourceFiles/iv/iv_prepare.cpp | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/Telegram/Resources/iv_html/page.css b/Telegram/Resources/iv_html/page.css index 00a3302bd..9c0ab832f 100644 --- a/Telegram/Resources/iv_html/page.css +++ b/Telegram/Resources/iv_html/page.css @@ -1053,6 +1053,9 @@ section.channel > a > h4 { display: block; margin: 0 auto; } +.media-outer { + margin-bottom: 16px; +} .photo-wrap, .video-wrap { width: 100%; diff --git a/Telegram/SourceFiles/iv/iv_prepare.cpp b/Telegram/SourceFiles/iv/iv_prepare.cpp index 699f44677..d5f537524 100644 --- a/Telegram/SourceFiles/iv/iv_prepare.cpp +++ b/Telegram/SourceFiles/iv/iv_prepare.cpp @@ -520,6 +520,9 @@ QByteArray Parser::block( }, result); if (!slideshow) { result += caption(data.vcaption()); + if (!collage) { + result = tag("div", { { "class", "media-outer" } }, result); + } } return result; } @@ -585,6 +588,9 @@ QByteArray Parser::block( } if (!slideshow) { result += caption(data.vcaption()); + if (!collage) { + result = tag("div", { { "class", "media-outer" } }, result); + } } return result; } From aff2be605e09288a67b00a81ed04c62a15bc37fd Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 26 Apr 2024 18:46:44 +0300 Subject: [PATCH 121/175] Removed item for poll creation from menu when it is impossible. --- .../SourceFiles/window/window_peer_menu.cpp | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 6860d3b0a..f3749d622 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -1085,6 +1085,34 @@ void Filler::addViewStatistics() { } void Filler::addCreatePoll() { + const auto isJoinChannel = [&] { + if (_request.section != Section::Replies) { + if (const auto c = _peer->asChannel(); c && !c->amIn()) { + return true; + } + } + return false; + }(); + const auto isBotStart = [&] { + const auto user = _peer ? _peer->asUser() : nullptr; + if (!user || !user->isBot()) { + return false; + } else if (!user->botInfo->startToken.isEmpty()) { + return true; + } + const auto history = _peer->owner().history(_peer); + if (history && history->isEmpty() && !history->lastMessage()) { + return true; + } + return false; + }(); + const auto isBlocked = [&] { + return _peer && _peer->isUser() && _peer->asUser()->isBlocked(); + }(); + if (isBlocked || isJoinChannel || isBotStart) { + return; + } + const auto can = _topic ? Data::CanSend(_topic, ChatRestriction::SendPolls) : _peer->canCreatePolls(); From 79532954dc885ec65b71095845acca3cbe76c235 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Apr 2024 20:16:38 +0400 Subject: [PATCH 122/175] Allow a bit more font size adjusting. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 6d1014fa8..cb57bef3f 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 6d1014fa8f67d4f997c0580a90df075008e0f34c +Subproject commit cb57bef3f01b7ec60eb0eae0ee68cd56cb3a9b1f From 372b3da09cb736e2293006452ad19bf4773e209b Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Apr 2024 20:23:08 +0400 Subject: [PATCH 123/175] Beta version 4.16.10. - Group admins can mass-moderate many messages. - Premium users can use animated emoji in polls. - Revert the default "Open Sans" font to 1.10. - Several crash fixes and small improvements. --- 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 ++++---- Telegram/lib_webview | 2 +- changelog.txt | 7 +++++++ 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 068e2c2bf..adc24216c 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.16.10.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 8536098db..07bbe9102 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 4,16,9,0 - PRODUCTVERSION 4,16,9,0 + FILEVERSION 4,16,10,0 + PRODUCTVERSION 4,16,10,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.16.9.0" + VALUE "FileVersion", "4.16.10.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.9.0" + VALUE "ProductVersion", "4.16.10.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 8ae0832aa..ae524c403 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 4,16,9,0 - PRODUCTVERSION 4,16,9,0 + FILEVERSION 4,16,10,0 + PRODUCTVERSION 4,16,10,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", "4.16.9.0" + VALUE "FileVersion", "4.16.10.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.9.0" + VALUE "ProductVersion", "4.16.10.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 97924b37d..271c39d52 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 = 4016009; -constexpr auto AppVersionStr = "4.16.9"; +constexpr auto AppVersion = 4016010; +constexpr auto AppVersionStr = "4.16.10"; constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index a2ce256db..620dc863c 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4016009 +AppVersion 4016010 AppVersionStrMajor 4.16 -AppVersionStrSmall 4.16.9 -AppVersionStr 4.16.9 +AppVersionStrSmall 4.16.10 +AppVersionStr 4.16.10 BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 4.16.9.beta +AppVersionOriginal 4.16.10.beta diff --git a/Telegram/lib_webview b/Telegram/lib_webview index 9f9bcaaec..2ccbfa5f3 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit 9f9bcaaec922644406faadda4d37014c9dec2dd9 +Subproject commit 2ccbfa5f3443274e40deb761674b536e2e8eedae diff --git a/changelog.txt b/changelog.txt index eb2b59480..649f042f3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +4.16.10 beta (26.04.24) + +- Group admins can mass-moderate many messages. +- Premium users can use animated emoji in polls. +- Revert the default "Open Sans" font to 1.10. +- Several crash fixes and small improvements. + 4.16.9 beta (23.04.24) - Show "Frequent contacts" when you focus the search field. From c0db5ee98ab3b536389d6bde0363c1030f7f5d87 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 26 Apr 2024 23:41:28 +0400 Subject: [PATCH 124/175] Beta version 4.16.10: Fix GCC build. --- Telegram/SourceFiles/boxes/moderate_messages_box.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp index 8117c7e77..0d1aa10a1 100644 --- a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp @@ -517,7 +517,6 @@ void CreateModerateMessagesBox( } wrap->toggle(!wrap->toggled(), anim::type::normal); { - const auto start = crl::now(); inner->heightValue() | rpl::start_with_next([=] { if (!wrap->animating()) { scrollLifetime->destroy(); From 6e67cfc7beb17e207c31a2b9d86aa184eb109fb2 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sat, 27 Apr 2024 12:26:34 +0300 Subject: [PATCH 125/175] Fixed possible crash in moderation box. --- .../boxes/moderate_messages_box.cpp | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp index 0d1aa10a1..5f08f98e3 100644 --- a/Telegram/SourceFiles/boxes/moderate_messages_box.cpp +++ b/Telegram/SourceFiles/boxes/moderate_messages_box.cpp @@ -185,7 +185,6 @@ void CreateModerateMessagesBox( rpl::event_stream checkAllRequests; Fn collectRequests; }; - constexpr auto kSmallDelayMs = 5; const auto options = CalculateModerateOptions(items); const auto inner = box->verticalLayout(); @@ -211,6 +210,7 @@ void CreateModerateMessagesBox( using Request = Fn, not_null)>; const auto sequentiallyRequest = [=](Request request, Users users) { + constexpr auto kSmallDelayMs = 5; const auto session = &items.front()->history()->session(); const auto history = items.front()->history(); const auto peerId = history->peer->id; @@ -243,14 +243,8 @@ void CreateModerateMessagesBox( not_null controller, Request request) { confirms->events() | rpl::start_with_next([=] { - if (checkbox->checked()) { - if (isSingle) { - const auto item = items.front(); - const auto channel = item->history()->peer->asChannel(); - request(users.front(), channel); - } else if (const auto collect = controller->collectRequests) { - sequentiallyRequest(request, collect()); - } + if (checkbox->checked() && controller->collectRequests) { + sequentiallyRequest(request, controller->collectRequests()); } }, checkbox->lifetime()); }; @@ -345,6 +339,11 @@ void CreateModerateMessagesBox( const auto appendList = [&]( not_null checkbox, not_null controller) { + if (isSingle) { + const auto user = users.front(); + controller->collectRequests = [=] { return Users{ user }; }; + return; + } const auto button = Ui::CreateChild