From 90fb59348cc3a228cc39d1df81b57d620d7f3a3d Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 29 Sep 2023 20:38:00 +0400 Subject: [PATCH 001/262] Try fixing Docker action. --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ad5ccedc5..947fc9178 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -30,7 +30,7 @@ jobs: curl -sSL https://install.python-poetry.org | python3 - - name: Free up some disk space. - uses: jlumbroso/free-disk-space@76866dbe54312617f00798d1762df7f43def6e5c + uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 - name: Docker image build. run: | From 70f22293cf7204c9eca6c2999b0b073137e07fd5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 29 Sep 2023 21:55:54 +0400 Subject: [PATCH 002/262] Get rid of last non-standard piece in Linux FileDialog getter --- .../platform/linux/file_utilities_linux.cpp | 25 ------------------- .../platform/linux/file_utilities_linux.h | 18 +++++++++++++ 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index 4f111a6e0..e4cf30fac 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -17,29 +17,4 @@ bool UnsafeShowOpenWith(const QString &filepath) { } } // namespace File - -namespace FileDialog { - -bool Get( - QPointer parent, - QStringList &files, - QByteArray &remoteContent, - const QString &caption, - const QString &filter, - ::FileDialog::internal::Type type, - QString startFile) { - if (parent) { - parent = parent->window(); - } - return ::FileDialog::internal::GetDefault( - parent, - files, - remoteContent, - caption, - filter, - type, - startFile); -} - -} // namespace FileDialog } // namespace Platform diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h index 76ccf00aa..2054d3f32 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h @@ -43,5 +43,23 @@ inline void InitLastPath() { ::FileDialog::internal::InitLastPathDefault(); } +inline bool Get( + QPointer parent, + QStringList &files, + QByteArray &remoteContent, + const QString &caption, + const QString &filter, + ::FileDialog::internal::Type type, + QString startFile) { + return ::FileDialog::internal::GetDefault( + parent, + files, + remoteContent, + caption, + filter, + type, + startFile); +} + } // namespace FileDialog } // namespace Platform From 7ddcc47fcd49850bfb8fbef9efb4f6f35ed1e0b9 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 29 Sep 2023 21:59:14 +0400 Subject: [PATCH 003/262] Make UnsafeShowOpenWith inline on Linux --- .../platform/linux/file_utilities_linux.cpp | 12 ------------ .../platform/linux/file_utilities_linux.h | 6 ++++++ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp index e4cf30fac..2f9c0d9f9 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.cpp @@ -6,15 +6,3 @@ 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 "platform/linux/linux_xdp_open_with_dialog.h" - -namespace Platform { -namespace File { - -bool UnsafeShowOpenWith(const QString &filepath) { - return internal::ShowXDPOpenWithDialog(filepath); -} - -} // 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 2054d3f32..baced1178 100644 --- a/Telegram/SourceFiles/platform/linux/file_utilities_linux.h +++ b/Telegram/SourceFiles/platform/linux/file_utilities_linux.h @@ -9,6 +9,8 @@ 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 { @@ -28,6 +30,10 @@ 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); } From a732d8f5e7ba517bd0af4598c7c37190c19b6dcd Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 1 Oct 2023 07:23:21 +0400 Subject: [PATCH 004/262] Disable vfork in Qt on Linux --- Telegram/build/docker/centos_env/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 1a92eb135..4f7b67e1a 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -767,6 +767,7 @@ RUN git clone -b {{ QT_TAG }} --depth=1 https://code.qt.io/qt/qt5.git qt_{{ QT } -qt-harfbuzz \ -qt-pcre \ -no-icu \ + INPUT_forkfd_pidfd=no \ -no-feature-xcb-sm \ -static \ -dbus-runtime \ From fe8ebc1659aed63ccc56ac66aa646768934a910c Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 1 Oct 2023 07:23:30 +0400 Subject: [PATCH 005/262] Update patches on Linux --- Telegram/build/docker/centos_env/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 4f7b67e1a..3194803e8 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -54,7 +54,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 6442ae042bb6d43391747f7413c7a88a6a37c7ef \ + && git fetch --depth=1 origin 413a12468bfdd02b9f1372c5c139753b0c5a432e \ && git reset --hard FETCH_HEAD \ && rm -rf .git From 9ad38b963835d3747168583cc3bc197df2febcad Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 1 Oct 2023 21:54:44 +0400 Subject: [PATCH 006/262] Update lib_base submodule. --- Telegram/lib_base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index cb8e07fa7..0912ba4b3 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit cb8e07fa7febcff1dbdbac7bdb0ac46e4eda5c8e +Subproject commit 0912ba4b3b67fc485dc43f64664b5a56b9509e02 From 5c4f006550226aafe8fcc87df827eb13ddcfffe5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 2 Oct 2023 01:40:53 +0400 Subject: [PATCH 007/262] Avoid Windows-specific hack to ruin initial main window geometry on Linux --- Telegram/SourceFiles/core/application.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 104d887c2..b5cbef8f4 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -512,14 +512,16 @@ void Application::startMediaView() { InvokeQueued(this, [=] { _mediaView = std::make_unique(); }); -#else // Q_OS_MAC +#elif defined Q_OS_WIN // Q_OS_MAC || Q_OS_WIN // On Windows we needed such hack for the main window, otherwise // somewhere inside the media viewer creating code its geometry // was broken / lost to some invalid values. const auto current = _lastActivePrimaryWindow->widget()->geometry(); _mediaView = std::make_unique(); _lastActivePrimaryWindow->widget()->Ui::RpWidget::setGeometry(current); -#endif // Q_OS_MAC +#else + _mediaView = std::make_unique(); +#endif // Q_OS_MAC || Q_OS_WIN } void Application::startTray() { From a757e07c3ac8656310edf83c6e6fc36c1520406f Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 2 Oct 2023 15:47:15 +0400 Subject: [PATCH 008/262] Line length clean up in notifications_manager_linux --- .../linux/notifications_manager_linux.cpp | 115 +++++++++--------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 935d85c2a..a4111bf02 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -42,6 +42,8 @@ constexpr auto kObjectPath = "/org/freedesktop/Notifications"; constexpr auto kInterface = kService; constexpr auto kPropertiesInterface = "org.freedesktop.DBus.Properties"; +using PropertyMap = std::map; + struct ServerInformation { Glib::ustring name; Glib::ustring vendor; @@ -123,7 +125,8 @@ void StartServiceAsync(Fn callback) { }; const auto errorName = - Gio::DBus::ErrorUtils::get_remote_error(e).raw(); + Gio::DBus::ErrorUtils::get_remote_error(e) + .raw(); if (!ranges::contains( NotSupportedErrors, @@ -192,21 +195,21 @@ void GetServerInformation(Fn callback) { Noexcept([&] { const auto reply = connection->call_finish(result); - const auto name = reply.get_child( - 0 - ).get_dynamic(); + const auto name = reply + .get_child(0) + .get_dynamic(); - const auto vendor = reply.get_child( - 1 - ).get_dynamic(); + const auto vendor = reply + .get_child(1) + .get_dynamic(); - const auto version = reply.get_child( - 2 - ).get_dynamic(); + const auto version = reply + .get_child(2) + .get_dynamic(); - const auto specVersion = reply.get_child( - 3 - ).get_dynamic(); + const auto specVersion = reply + .get_child(3) + .get_dynamic(); callback(ServerInformation{ name, @@ -241,11 +244,9 @@ void GetCapabilities(Fn &)> callback) { Core::Sandbox::Instance().customEnterFromEventLoop([&] { Noexcept([&] { callback( - connection->call_finish( - result - ).get_child( - 0 - ).get_dynamic>() + connection->call_finish(result) + .get_child(0) + .get_dynamic>() ); }, [&] { callback({}); @@ -275,12 +276,10 @@ void GetInhibited(Fn callback) { Core::Sandbox::Instance().customEnterFromEventLoop([&] { Noexcept([&] { callback( - connection->call_finish( - result - ).get_child( - 0 - ).get_dynamic>( - ).get() + connection->call_finish(result) + .get_child(0) + .get_dynamic>() + .get() ); }, [&] { callback(false); @@ -458,35 +457,43 @@ bool NotificationData::init( Core::Sandbox::Instance().customEnterFromEventLoop([&] { Noexcept([&] { if (signal_name == "ActionInvoked") { - const auto id = parameters.get_child(0).get_dynamic(); + const auto id = parameters + .get_child(0) + .get_dynamic(); - const auto actionName = parameters.get_child( - 1 - ).get_dynamic(); + const auto actionName = parameters + .get_child(1) + .get_dynamic(); actionInvoked(id, actionName); } else if (signal_name == "ActivationToken") { - const auto id = parameters.get_child(0).get_dynamic(); + const auto id = parameters + .get_child(0) + .get_dynamic(); - const auto token = parameters.get_child( - 1 - ).get_dynamic(); + const auto token = parameters + .get_child(1) + .get_dynamic(); activationToken(id, token); } else if (signal_name == "NotificationReplied") { - const auto id = parameters.get_child(0).get_dynamic(); + const auto id = parameters + .get_child(0) + .get_dynamic(); - const auto text = parameters.get_child( - 1 - ).get_dynamic(); + const auto text = parameters + .get_child(1) + .get_dynamic(); notificationReplied(id, text); } else if (signal_name == "NotificationClosed") { - const auto id = parameters.get_child(0).get_dynamic(); + const auto id = parameters + .get_child(0) + .get_dynamic(); - const auto reason = parameters.get_child( - 1 - ).get_dynamic(); + const auto reason = parameters + .get_child(1) + .get_dynamic(); notificationClosed(id, reason); } @@ -637,14 +644,13 @@ void NotificationData::show() { _hints, -1, }), - crl::guard(weak, [=](const Glib::RefPtr &result) { + crl::guard(weak, [=]( + const Glib::RefPtr &result) { Core::Sandbox::Instance().customEnterFromEventLoop([&] { Noexcept([&] { - _notificationId = connection->call_finish( - result - ).get_child( - 0 - ).get_dynamic(); + _notificationId = connection->call_finish(result) + .get_child(0) + .get_dynamic(); }, [&] { _manager->clearNotification(_id); }); @@ -966,20 +972,19 @@ Manager::Private::Private(not_null manager) const Glib::VariantContainerBase ¶meters) { Core::Sandbox::Instance().customEnterFromEventLoop([&] { Noexcept([&] { - const auto interface = parameters.get_child( - 0 - ).get_dynamic(); + const auto interface = parameters + .get_child(0) + .get_dynamic(); if (interface != kInterface) { return; } - _inhibited = parameters.get_child( - 1 - ).get_dynamic>( - ).at( - "Inhibited" - ).get_dynamic(); + _inhibited = parameters + .get_child(1) + .get_dynamic() + .at("Inhibited") + .get_dynamic(); }); }); }), From 99f4b93745116b189d2aa9657846f1ebefa20b0f Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 2 Oct 2023 17:54:15 +0400 Subject: [PATCH 009/262] Attempt to fix build with Clang on Linux. --- .../linux/notifications_manager_linux.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index a4111bf02..02aa22751 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -55,6 +55,10 @@ bool ServiceRegistered = false; ServerInformation CurrentServerInformation; std::vector CurrentCapabilities; +[[nodiscard]] bool HasCapability(const char *value) { + return ranges::contains(CurrentCapabilities, value, &Glib::ustring::raw); +} + void Noexcept(Fn callback, Fn failed = nullptr) noexcept { try { callback(); @@ -445,7 +449,6 @@ bool NotificationData::init( } const auto weak = base::make_weak(this); - const auto &capabilities = CurrentCapabilities; const auto signalEmitted = crl::guard(weak, [=]( const Glib::RefPtr &connection, @@ -503,7 +506,7 @@ bool NotificationData::init( _imageKey = GetImageKey(CurrentServerInformation.specVersion); - if (ranges::contains(capabilities, "body-markup")) { + if (HasCapability("body-markup")) { _title = title.toStdString(); _body = subtitle.isEmpty() @@ -519,7 +522,7 @@ bool NotificationData::init( _body = msg.toStdString(); } - if (ranges::contains(capabilities, "actions")) { + if (HasCapability("actions")) { _actions.push_back("default"); _actions.push_back(tr::lng_open_link(tr::now).toStdString()); @@ -530,7 +533,7 @@ bool NotificationData::init( tr::lng_context_mark_read(tr::now).toStdString()); } - if (ranges::contains(capabilities, "inline-reply") + if (HasCapability("inline-reply") && !options.hideReplyButton) { _actions.push_back("inline-reply"); _actions.push_back( @@ -560,13 +563,13 @@ bool NotificationData::init( kObjectPath); } - if (ranges::contains(capabilities, "action-icons")) { + if (HasCapability("action-icons")) { _hints["action-icons"] = Glib::create_variant(true); } // suppress system sound if telegram sound activated, // otherwise use system sound - if (ranges::contains(capabilities, "sound")) { + if (HasCapability("sound")) { if (Core::App().settings().soundNotify()) { _hints["suppress-sound"] = Glib::create_variant(true); } else { @@ -576,7 +579,7 @@ bool NotificationData::init( } } - if (ranges::contains(capabilities, "x-canonical-append")) { + if (HasCapability("x-canonical-append")) { _hints["x-canonical-append"] = Glib::create_variant( Glib::ustring("true")); } @@ -822,7 +825,7 @@ bool ByDefault() { // (no, using sound capability is not a way) "inhibitions", }, [](const auto *capability) { - return ranges::contains(CurrentCapabilities, capability); + return HasCapability(capability); }); } @@ -915,7 +918,6 @@ private: Manager::Private::Private(not_null manager) : _manager(manager) { const auto &serverInformation = CurrentServerInformation; - const auto &capabilities = CurrentCapabilities; if (!serverInformation.name.empty()) { LOG(("Notification daemon product name: %1") @@ -937,17 +939,17 @@ Manager::Private::Private(not_null manager) .arg(serverInformation.specVersion.toString())); } - if (!capabilities.empty()) { + if (!CurrentCapabilities.empty()) { LOG(("Notification daemon capabilities: %1").arg( ranges::fold_left( - capabilities, + CurrentCapabilities, "", [](const Glib::ustring &a, const Glib::ustring &b) { return a + (a.empty() ? "" : ", ") + b; }).c_str())); } - if (ranges::contains(capabilities, "inhibitions")) { + if (HasCapability("inhibitions")) { Noexcept([&] { _dbusConnection = Gio::DBus::Connection::get_sync( Gio::DBus::BusType::SESSION); From 871fef2c4ae2bf9eab10d1d44b25d4d0fdaee9ea Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 2 Oct 2023 17:54:49 +0400 Subject: [PATCH 010/262] Version 4.10.3. - Fix crash on external link opening. (Linux only) --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 4 ++-- Telegram/build/version | 8 ++++---- changelog.txt | 6 +++++- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index a64e3b239..9baf83b8e 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.10.3.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 95cc494ec..4fd7fb0e2 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,10,2,0 - PRODUCTVERSION 4,10,2,0 + FILEVERSION 4,10,3,0 + PRODUCTVERSION 4,10,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.10.2.0" + VALUE "FileVersion", "4.10.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.10.2.0" + VALUE "ProductVersion", "4.10.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 8c0205793..aad413127 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,10,2,0 - PRODUCTVERSION 4,10,2,0 + FILEVERSION 4,10,3,0 + PRODUCTVERSION 4,10,3,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "4.10.2.0" + VALUE "FileVersion", "4.10.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2023" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.10.2.0" + VALUE "ProductVersion", "4.10.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 85e2dee9d..3b5aca991 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 = 4010002; -constexpr auto AppVersionStr = "4.10.2"; +constexpr auto AppVersion = 4010003; +constexpr auto AppVersionStr = "4.10.3"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index aa5d6c6c7..cb6a420ca 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4010002 +AppVersion 4010003 AppVersionStrMajor 4.10 -AppVersionStrSmall 4.10.2 -AppVersionStr 4.10.2 +AppVersionStrSmall 4.10.3 +AppVersionStr 4.10.3 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 4.10.2 +AppVersionOriginal 4.10.3 diff --git a/changelog.txt b/changelog.txt index 821c95794..446707c35 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +4.10.3 (02.10.23) + +- Fix crash on external link opening. (Linux only) + 4.10.2 (28.09.23) - Bug fixes and other minor improvements. @@ -112,7 +116,7 @@ 4.8.3 (31.05.23) - Fix main window focus from notifications with disabled animations. -- Some minor fixes and improvements. +- Some minor fixes and improvements. 4.8.2 (16.05.23) From ac520b314db13b8ad9cdc08e6ecb90cc64faf6f8 Mon Sep 17 00:00:00 2001 From: "Sergey A. Osokin" Date: Mon, 2 Oct 2023 10:44:27 -0400 Subject: [PATCH 011/262] Fix build on FreeBSD --- .../SourceFiles/platform/linux/notifications_manager_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 02aa22751..ad0ca8fd7 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -814,7 +814,7 @@ bool ByDefault() { // A list of capabilities that offer feature parity // with custom notifications - return ranges::all_of(std::initializer_list{ + return ranges::all_of(std::array{ // To show message content "body", // To have buttons on notifications From 28785330782e003bfa86302e4b61cd768f2ed881 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 4 Oct 2023 02:27:35 +0400 Subject: [PATCH 012/262] Re-throw original exception in Linux notification's StartServiceAsync --- .../SourceFiles/platform/linux/notifications_manager_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index ad0ca8fd7..eddd32dc8 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -135,7 +135,7 @@ void StartServiceAsync(Fn callback) { if (!ranges::contains( NotSupportedErrors, errorName)) { - throw e; + throw; } } }); From 54efa2353e6b6f575ac5d95201abad0c695d7956 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 4 Oct 2023 12:06:18 +0400 Subject: [PATCH 013/262] Update submodules --- Telegram/lib_ui | 2 +- Telegram/lib_webview | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index eac439713..93458e4cb 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit eac439713b35f11e13d941d5f047712c0c4e70d3 +Subproject commit 93458e4cb3845c9743bb0c0a78f1e6ce7ee99363 diff --git a/Telegram/lib_webview b/Telegram/lib_webview index 5c4f890f3..f1a829c56 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit 5c4f890f356f1ccb1401581b2a960b35455a7488 +Subproject commit f1a829c569f3e3bc8189d7411373a9bed6877bd2 From ef2a0bb05e8a18596c6ff12ce20b980a1a1e3dcb Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 3 Oct 2023 21:55:47 +0400 Subject: [PATCH 014/262] Add libnvidia-egl-wayland1 to snap --- snap/snapcraft.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 2ab1c0188..465d3f4b0 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -196,6 +196,7 @@ parts: stage-packages: - libc-bin - libgdk-pixbuf-2.0-0 + - libnvidia-egl-wayland1 - libxkbcommon0 - locales-all - shared-mime-info From f881192dd08cc399ceebc202bdb57ebb7ca4e006 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 3 Oct 2023 20:26:24 +0400 Subject: [PATCH 015/262] Don't enter settings after update --- Telegram/SourceFiles/platform/linux/launcher_linux.cpp | 9 +++++++-- Telegram/SourceFiles/platform/linux/launcher_linux.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp index 1e21f57fc..55236a156 100644 --- a/Telegram/SourceFiles/platform/linux/launcher_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/launcher_linux.cpp @@ -43,6 +43,9 @@ bool Launcher::launchUpdater(UpdaterLaunch action) { } const auto justRelaunch = action == UpdaterLaunch::JustRelaunch; + if (action == UpdaterLaunch::PerformUpdate) { + _updating = true; + } std::vector argumentsList; @@ -82,8 +85,10 @@ bool Launcher::launchUpdater(UpdaterLaunch action) { argumentsList.push_back("-key"); argumentsList.push_back(cDataFile().toStdString()); } - argumentsList.push_back("-noupdate"); - argumentsList.push_back("-tosettings"); + if (!_updating) { + argumentsList.push_back("-noupdate"); + argumentsList.push_back("-tosettings"); + } if (customWorkingDir()) { argumentsList.push_back("-workdir"); argumentsList.push_back(cWorkingDir().toStdString()); diff --git a/Telegram/SourceFiles/platform/linux/launcher_linux.h b/Telegram/SourceFiles/platform/linux/launcher_linux.h index e0a45af0f..6c8f28e91 100644 --- a/Telegram/SourceFiles/platform/linux/launcher_linux.h +++ b/Telegram/SourceFiles/platform/linux/launcher_linux.h @@ -21,6 +21,8 @@ private: void initHook() override; bool launchUpdater(UpdaterLaunch action) override; + bool _updating = false; + }; } // namespace Platform From 6ba922d7b0d7c41d8e43ad93e10dc3a14481a88d Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 29 Sep 2023 21:13:32 +0400 Subject: [PATCH 016/262] Fix channel stories open from chats list. --- Telegram/SourceFiles/dialogs/dialogs_widget.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 60dbe006e..7bb0fb805 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -536,14 +536,12 @@ void Widget::chosenRow(const ChosenRow &row) { return; } else if (history) { const auto peer = history->peer; - if (const auto user = peer->asUser()) { - if (row.message.fullId.msg == ShowAtUnreadMsgId) { - if (row.userpicClick - && user->hasActiveStories() - && !user->isSelf()) { - controller()->openPeerStories(user->id); - return; - } + if (row.message.fullId.msg == ShowAtUnreadMsgId) { + if (row.userpicClick + && peer->hasActiveStories() + && !peer->isSelf()) { + controller()->openPeerStories(peer->id); + return; } } const auto showAtMsgId = controller()->uniqueChatsInSearchResults() From 5ef48cac9cb0576d31d3c15910b4b5e0e9e07858 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 29 Sep 2023 09:38:17 +0400 Subject: [PATCH 017/262] Use shared_ptr as settings section id. --- .../info/settings/info_settings_widget.cpp | 2 +- .../settings_cloud_password_common.h | 2 +- .../SourceFiles/settings/settings_common.h | 14 ++++---- .../settings/settings_local_passcode.cpp | 2 +- .../settings/settings_notifications.cpp | 4 +-- .../settings/settings_notifications_type.cpp | 32 +++++++++---------- .../settings/settings_notifications_type.h | 25 ++++----------- Telegram/SourceFiles/settings/settings_type.h | 4 +-- 8 files changed, 37 insertions(+), 48 deletions(-) diff --git a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp index 58e5e65f1..42633f1fe 100644 --- a/Telegram/SourceFiles/info/settings/info_settings_widget.cpp +++ b/Telegram/SourceFiles/info/settings/info_settings_widget.cpp @@ -45,7 +45,7 @@ Widget::Widget( , _self(controller->key().settingsSelf()) , _type(controller->section().settingsType()) , _inner([&] { - auto inner = _type()->create(this, controller->parentController()); + auto inner = _type->create(this, controller->parentController()); if (inner->hasFlexibleTopBar()) { auto filler = setInnerWidget(object_ptr(this)); filler->resize(1, 1); diff --git a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h index ed1d94c7c..d18bca048 100644 --- a/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h +++ b/Telegram/SourceFiles/settings/cloud_password/settings_cloud_password_common.h @@ -162,7 +162,7 @@ public: } [[nodiscard]] static Type Id() { - return &SectionMetaImplementation::Meta; + return SectionFactory::Instance(); } [[nodiscard]] Type id() const final override { return Id(); diff --git a/Telegram/SourceFiles/settings/settings_common.h b/Telegram/SourceFiles/settings/settings_common.h index d601a1c1d..0a7a48384 100644 --- a/Telegram/SourceFiles/settings/settings_common.h +++ b/Telegram/SourceFiles/settings/settings_common.h @@ -52,14 +52,16 @@ using Button = Ui::SettingsButton; class AbstractSection; -struct SectionMeta { +struct AbstractSectionFactory { [[nodiscard]] virtual object_ptr create( not_null parent, not_null controller) const = 0; + + virtual ~AbstractSectionFactory() = default; }; template -struct SectionMetaImplementation : SectionMeta { +struct SectionFactory : AbstractSectionFactory { object_ptr create( not_null parent, not_null controller @@ -67,9 +69,9 @@ struct SectionMetaImplementation : SectionMeta { return object_ptr(parent, controller); } - [[nodiscard]] static not_null Meta() { - static SectionMetaImplementation result; - return &result; + [[nodiscard]] static const std::shared_ptr &Instance() { + static const auto result = std::make_shared(); + return result; } }; @@ -120,7 +122,7 @@ public: using AbstractSection::AbstractSection; [[nodiscard]] static Type Id() { - return &SectionMetaImplementation::Meta; + return SectionFactory::Instance(); } [[nodiscard]] Type id() const final override { return Id(); diff --git a/Telegram/SourceFiles/settings/settings_local_passcode.cpp b/Telegram/SourceFiles/settings/settings_local_passcode.cpp index 49e8ba019..77dad4027 100644 --- a/Telegram/SourceFiles/settings/settings_local_passcode.cpp +++ b/Telegram/SourceFiles/settings/settings_local_passcode.cpp @@ -330,7 +330,7 @@ public: } [[nodiscard]] static Type Id() { - return &SectionMetaImplementation::Meta; + return SectionFactory::Instance(); } [[nodiscard]] Type id() const final override { return Id(); diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index 246e67450..63d7c0160 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -171,7 +171,7 @@ void AddTypeButton( st::settingsNotificationType, { icon }); button->setClickedCallback([=] { - showOther(NotificationsTypeId(type)); + showOther(NotificationsType::Id(type)); }); const auto session = &controller->session(); @@ -290,7 +290,7 @@ void AddTypeButton( tr::lng_notification_exceptions_view(), [=] { box->closeBox(); - showOther(NotificationsTypeId(type)); + showOther(NotificationsType::Id(type)); }); })); } diff --git a/Telegram/SourceFiles/settings/settings_notifications_type.cpp b/Telegram/SourceFiles/settings/settings_notifications_type.cpp index 5f394face..f1240938f 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_type.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications_type.cpp @@ -36,6 +36,20 @@ namespace { using Notify = Data::DefaultNotify; +struct Factory : AbstractSectionFactory { + explicit Factory(Notify type) : type(type) { + } + + object_ptr create( + not_null parent, + not_null controller + ) const final override { + return object_ptr(parent, controller, type); + } + + const Notify type = {}; +}; + class AddExceptionBoxController final : public ChatsListBoxController , public base::has_weak_ptr { @@ -351,11 +365,6 @@ void ExceptionsController::sort() { delegate()->peerListSortRows(predicate); } -template -[[nodiscard]] Type Id() { - return &NotificationsTypeMetaImplementation::Meta; -} - [[nodiscard]] rpl::producer Title(Notify type) { switch (type) { case Notify::User: return tr::lng_notification_title_private_chats(); @@ -562,15 +571,6 @@ void SetupExceptions( } // namespace -Type NotificationsTypeId(Notify type) { - switch (type) { - case Notify::User: return Id(); - case Notify::Group: return Id(); - case Notify::Broadcast: return Id(); - } - Unexpected("Type in NotificationTypeId."); -} - NotificationsType::NotificationsType( QWidget *parent, not_null controller, @@ -589,8 +589,8 @@ rpl::producer NotificationsType::title() { Unexpected("Type in NotificationsType."); } -Type NotificationsType::id() const { - return NotificationsTypeId(_type); +Type NotificationsType::Id(Notify type) { + return std::make_shared(type); } void NotificationsType::setupContent( diff --git a/Telegram/SourceFiles/settings/settings_notifications_type.h b/Telegram/SourceFiles/settings/settings_notifications_type.h index 59014f0d5..c5f6d95d4 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_type.h +++ b/Telegram/SourceFiles/settings/settings_notifications_type.h @@ -25,32 +25,19 @@ public: [[nodiscard]] rpl::producer title() override; - [[nodiscard]] Type id() const final override; + [[nodiscard]] static Type Id(Data::DefaultNotify type); + + [[nodiscard]] Type id() const final override { + return Id(_type); + } private: void setupContent(not_null controller); - Data::DefaultNotify _type; + const Data::DefaultNotify _type; }; -template -struct NotificationsTypeMetaImplementation : SectionMeta { - object_ptr create( - not_null parent, - not_null controller - ) const final override { - return object_ptr(parent, controller, kType); - } - - [[nodiscard]] static not_null Meta() { - static NotificationsTypeMetaImplementation result; - return &result; - } -}; - -[[nodiscard]] Type NotificationsTypeId(Data::DefaultNotify type); - [[nodiscard]] bool NotificationsEnabledForType( not_null session, Data::DefaultNotify type); diff --git a/Telegram/SourceFiles/settings/settings_type.h b/Telegram/SourceFiles/settings/settings_type.h index 76b8d9840..ee5460916 100644 --- a/Telegram/SourceFiles/settings/settings_type.h +++ b/Telegram/SourceFiles/settings/settings_type.h @@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Settings { -struct SectionMeta; -using Type = not_null(*)(); +struct AbstractSectionFactory; +using Type = std::shared_ptr; } // namespace Settings From 2b3f17e982fa96d1996063e4b45c1faeb0843b01 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 29 Sep 2023 13:09:45 +0300 Subject: [PATCH 018/262] Removed call button from history with service user. --- .../SourceFiles/history/view/history_view_top_bar_widget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 577ebaff8..c026ab9c7 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -1101,7 +1101,9 @@ void TopBarWidget::updateControlsVisibility() { const auto callsEnabled = [&] { if (const auto peer = _activeChat.key.peer()) { if (const auto user = peer->asUser()) { - return !user->isSelf() && !user->isBot(); + return !user->isSelf() + && !user->isBot() + && !peer->isServiceUser(); } } return false; From 396c229a4d857a120659f86e647f7e26acd811c1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 3 Oct 2023 17:50:33 +0400 Subject: [PATCH 019/262] Improve Ui::Text::String features. --- Telegram/SourceFiles/boxes/create_poll_box.cpp | 2 +- Telegram/SourceFiles/data/data_folder.cpp | 2 +- Telegram/SourceFiles/data/data_media_types.cpp | 10 +++++----- Telegram/SourceFiles/data/data_story.cpp | 4 ++-- .../SourceFiles/dialogs/ui/dialogs_layout.cpp | 10 +++++----- .../dialogs/ui/dialogs_message_view.cpp | 18 ++++++++++++------ .../dialogs/ui/dialogs_topics_view.cpp | 4 ++-- .../export/view/export_view_top_bar.cpp | 2 +- Telegram/SourceFiles/history/history_item.cpp | 6 ++---- .../history/history_item_components.cpp | 2 +- .../SourceFiles/history/history_widget.cpp | 2 +- .../controls/history_view_compose_controls.cpp | 2 +- .../controls/history_view_forward_panel.cpp | 4 ++-- .../view/history_view_service_message.cpp | 8 ++++---- .../view/history_view_service_message.h | 2 +- .../history/view/media/history_view_game.cpp | 2 +- .../view/media/history_view_web_page.cpp | 4 +++- .../info/profile/info_profile_cover.cpp | 4 ++-- Telegram/SourceFiles/lang/lang_instance.cpp | 10 ++++++---- Telegram/SourceFiles/lang/lang_tag.cpp | 6 ++++-- Telegram/SourceFiles/lang/lang_tag.h | 3 ++- .../media/view/media_view_overlay_widget.cpp | 2 +- .../SourceFiles/payments/ui/payments_panel.cpp | 12 ++++-------- .../ui/chat/attach/attach_bot_webview.cpp | 12 ++++-------- Telegram/SourceFiles/ui/chat/chat_style.cpp | 6 +++--- Telegram/SourceFiles/ui/chat/message_bar.cpp | 2 +- Telegram/SourceFiles/ui/empty_userpic.cpp | 2 +- Telegram/SourceFiles/ui/text/text_options.cpp | 2 +- .../window/notifications_manager_default.cpp | 10 +++++----- .../window/themes/window_theme_preview.cpp | 10 +++++----- Telegram/lib_ui | 2 +- 31 files changed, 85 insertions(+), 82 deletions(-) diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 3274e4eeb..ef53b2d62 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -194,7 +194,7 @@ not_null CreateWarningLabel( if (value >= 0) { result->setText(QString::number(value)); } else { - result->setMarkedText(Ui::Text::PlainLink( + result->setMarkedText(Ui::Text::Colorized( QString::number(value))); } result->setVisible(shown); diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index 743487c85..bbd4ce987 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -75,7 +75,7 @@ constexpr auto kShowChatNamesCount = 8; .entities = (history->chatListBadgesState().unread ? EntitiesInText{ { EntityType::Semibold, 0, int(name.size()), QString() }, - { EntityType::PlainLink, 0, int(name.size()), QString() }, + { EntityType::Colorized, 0, int(name.size()), QString() }, } : EntitiesInText{}), }; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 7a36a5515..6674c76b2 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -85,7 +85,7 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; const TextWithEntities &caption, bool hasMiniImages = false) { if (caption.text.isEmpty()) { - return Ui::Text::PlainLink(attachType); + return Ui::Text::Colorized(attachType); } return hasMiniImages @@ -96,7 +96,7 @@ using ItemPreviewImage = HistoryView::ItemPreviewImage; tr::lng_dialogs_text_media_wrapped( tr::now, lt_media, - Ui::Text::PlainLink(attachType), + Ui::Text::Colorized(attachType), Ui::Text::WithEntities), lt_caption, caption, @@ -558,7 +558,7 @@ ItemPreview Media::toGroupPreview( : fileCount ? tr::lng_in_dlg_file_count(tr::now, lt_count, fileCount) : tr::lng_in_dlg_album(tr::now); - result.text = Ui::Text::PlainLink(genericText); + result.text = Ui::Text::Colorized(genericText); } if (!loadingContext.empty()) { result.loadingContext = std::move(loadingContext); @@ -937,7 +937,7 @@ TextWithEntities MediaFile::notificationText() const { const auto text = _emoji.isEmpty() ? tr::lng_in_dlg_sticker(tr::now) : tr::lng_in_dlg_sticker_emoji(tr::now, lt_emoji, _emoji); - return Ui::Text::PlainLink(text); + return Ui::Text::Colorized(text); } const auto type = [&] { if (_document->isVideoMessage()) { @@ -1719,7 +1719,7 @@ PollData *MediaPoll::poll() const { } TextWithEntities MediaPoll::notificationText() const { - return Ui::Text::PlainLink(_poll->question); + return Ui::Text::Colorized(_poll->question); } QString MediaPoll::pinnedTextSubstring() const { diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp index 13c453e06..6aba706b9 100644 --- a/Telegram/SourceFiles/data/data_story.cpp +++ b/Telegram/SourceFiles/data/data_story.cpp @@ -308,14 +308,14 @@ Image *Story::replyPreview() const { TextWithEntities Story::inReplyText() const { const auto type = tr::lng_in_dlg_story(tr::now); return _caption.text.isEmpty() - ? Ui::Text::PlainLink(type) + ? Ui::Text::Colorized(type) : tr::lng_dialogs_text_media( tr::now, lt_media_part, tr::lng_dialogs_text_media_wrapped( tr::now, lt_media, - Ui::Text::PlainLink(type), + Ui::Text::Colorized(type), Ui::Text::WithEntities), lt_caption, _caption, diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index 3ef60a4fe..4b9c3fe87 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -260,7 +260,7 @@ void PaintFolderEntryText( .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler), - .elisionLines = rect.height() / st::dialogsTextFont->height, + .elisionHeight = rect.height(), }); } @@ -420,7 +420,7 @@ void PaintRow( .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler), - .elisionLines = 1, + .elisionOneLine = true, }); } else if (draft || (supportMode @@ -462,13 +462,13 @@ void PaintRow( auto &cache = thread->cloudDraftTextCache(); if (cache.isEmpty()) { using namespace TextUtilities; - auto draftWrapped = Text::PlainLink( + auto draftWrapped = Text::Colorized( tr::lng_dialogs_text_from_wrapped( tr::now, lt_from, tr::lng_from_draft(tr::now))); auto draftText = supportMode - ? Text::PlainLink( + ? Text::Colorized( Support::ChatOccupiedString(history)) : tr::lng_dialogs_text_with_from( tr::now, @@ -514,7 +514,7 @@ void PaintRow( .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler), - .elisionLines = 1, + .elisionOneLine = true, }); } } else if (!item) { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp index f40bf5308..1202d5aaf 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp @@ -94,7 +94,7 @@ TextWithEntities DialogsPreviewText(TextWithEntities text) { EntityType::Underline, EntityType::Italic, EntityType::CustomEmoji, - EntityType::PlainLink, + EntityType::Colorized, }); for (auto &entity : result.entities) { if (entity.type() == EntityType::Pre) { @@ -102,6 +102,13 @@ TextWithEntities DialogsPreviewText(TextWithEntities text) { EntityType::Code, entity.offset(), entity.length()); + } else if (entity.type() == EntityType::Colorized + && !entity.data().isEmpty()) { + // Drop 'data' so that only link-color colorization takes place. + entity = EntityInText( + EntityType::Colorized, + entity.offset(), + entity.length()); } } return result; @@ -188,7 +195,7 @@ void MessageView::prepare( TextUtilities::Trim(preview.text); auto textToCache = DialogsPreviewText(std::move(preview.text)); _hasPlainLinkAtBegin = !textToCache.entities.empty() - && (textToCache.entities.front().type() == EntityType::PlainLink); + && (textToCache.entities.front().type() == EntityType::Colorized); _textCache.setMarkedText( st::dialogsTextStyle, std::move(textToCache), @@ -305,7 +312,6 @@ void MessageView::paint( rect.setWidth(rect.width() - st::forumDialogJumpArrowSkip); finalRight -= st::forumDialogJumpArrowSkip; } - const auto lines = rect.height() / st::dialogsTextFont->height; const auto pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler); if (!_senderCache.isEmpty()) { @@ -313,7 +319,7 @@ void MessageView::paint( .position = rect.topLeft(), .availableWidth = rect.width(), .palette = palette, - .elisionLines = lines, + .elisionHeight = rect.height(), }); rect.setLeft(rect.x() + _senderCache.maxWidth()); if (!_imagesCache.empty() && !_leftIcon) { @@ -381,7 +387,7 @@ void MessageView::paint( .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = pausedSpoiler, - .elisionLines = lines, + .elisionHeight = rect.height(), }); rect.setLeft(rect.x() + _textCache.maxWidth()); } @@ -457,7 +463,7 @@ HistoryView::ItemPreview PreviewWithSender( auto fullWithOffset = tr::lng_dialogs_text_with_from( tr::now, lt_from_part, - Ui::Text::PlainLink(std::move(wrappedWithOffset.text)), + Ui::Text::Colorized(std::move(wrappedWithOffset.text)), lt_message, std::move(preview.text), TextWithTagOffset::FromString); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp index 20d74e916..f9fe16062 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp @@ -75,7 +75,7 @@ void TopicsView::prepare(MsgId frontRootId, Fn customEmojiRepaint) { title.title.setMarkedText( st::dialogsTextStyle, (unread - ? Ui::Text::PlainLink( + ? Ui::Text::Colorized( Ui::Text::Wrapped( std::move(topicTitle), EntityType::Bold)) @@ -141,7 +141,7 @@ void TopicsView::paint( .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler), - .elisionLines = 1, + .elisionOneLine = true, }); const auto skip = skipBig ? context.st->topicsSkipBig diff --git a/Telegram/SourceFiles/export/view/export_view_top_bar.cpp b/Telegram/SourceFiles/export/view/export_view_top_bar.cpp index 62f65e81e..dbdb9fc6e 100644 --- a/Telegram/SourceFiles/export/view/export_view_top_bar.cpp +++ b/Telegram/SourceFiles/export/view/export_view_top_bar.cpp @@ -45,7 +45,7 @@ void TopBar::updateData(Content &&content) { .append(" \xe2\x80\x93 ") .append(row.label) .append(' ') - .append(Ui::Text::PlainLink(row.info))); + .append(Ui::Text::Colorized(row.info))); _progress->setValue(row.progress); } diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index c4c7e3546..746be6038 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -2932,9 +2932,7 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const { // Because larger version is shown exactly to the left of the small. //auto media = _media ? _media->toPreview(options) : ItemPreview(); return { - .text = Ui::Text::Wrapped( - notificationText(), - EntityType::PlainLink), + .text = Ui::Text::Colorized(notificationText()), //.images = std::move(media.images), //.loadingContext = std::move(media.loadingContext), }; @@ -3011,7 +3009,7 @@ TextWithEntities HistoryItem::inReplyText() const { result = Ui::Text::Mid(result, name.size()); TextUtilities::Trim(result); } - return Ui::Text::Wrapped(result, EntityType::PlainLink); + return Ui::Text::Colorized(result); } const std::vector &HistoryItem::customTextLinks() const { diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp index 0edbbeeff..402f32127 100644 --- a/Telegram/SourceFiles/history/history_item_components.cpp +++ b/Telegram/SourceFiles/history/history_item_components.cpp @@ -630,7 +630,7 @@ void HistoryMessageReply::paint( .pausedEmoji = (context.paused || On(PowerSaving::kEmojiChat)), .pausedSpoiler = pausedSpoiler, - .elisionLines = 1, + .elisionOneLine = true, }); p.setTextPalette(stm->textPalette); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index c1dbc9911..8e2f08809 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -7949,7 +7949,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { .now = now, .pausedEmoji = paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = pausedSpoiler, - .elisionLines = 1, + .elisionOneLine = true, }); } else { p.setFont(st::msgDateFont); 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 cb1affa57..4f43bac26 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -902,7 +902,7 @@ void FieldHeader::paintEditOrReplyToMessage(Painter &p) { .now = crl::now(), .pausedEmoji = p.inactive() || On(PowerSaving::kEmojiChat), .pausedSpoiler = p.inactive() || On(PowerSaving::kChatSpoiler), - .elisionLines = 1, + .elisionOneLine = true, }); } diff --git a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp index b00624867..136d52712 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp @@ -181,7 +181,7 @@ void ForwardPanel::updateTexts() { text = DropCustomEmoji(std::move(text)); } } else { - text = Ui::Text::PlainLink( + text = Ui::Text::Colorized( tr::lng_forward_messages(tr::now, lt_count, count)); } } @@ -364,7 +364,7 @@ void ForwardPanel::paint( .now = now, .pausedEmoji = paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = pausedSpoiler, - .elisionLines = 1, + .elisionOneLine = true, }); } diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.cpp b/Telegram/SourceFiles/history/view/history_view_service_message.cpp index d1d7953a6..44ddb1c17 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_service_message.cpp @@ -358,15 +358,15 @@ void ServiceMessagePainter::PaintComplexBubble( } } -QVector ServiceMessagePainter::CountLineWidths( +std::vector ServiceMessagePainter::CountLineWidths( const Ui::Text::String &text, const QRect &textRect) { const auto linesCount = qMax( textRect.height() / st::msgServiceFont->height, 1); - auto result = QVector(); - result.reserve(linesCount); - text.countLineWidths(textRect.width(), &result); + auto result = text.countLineWidths(textRect.width(), { + .reserve = linesCount, + }); const auto minDelta = 2 * (Ui::HistoryServiceMsgRadius() + Ui::HistoryServiceMsgInvertedRadius() diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.h b/Telegram/SourceFiles/history/view/history_view_service_message.h index 5ee09aa7e..4fcd5748f 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.h +++ b/Telegram/SourceFiles/history/view/history_view_service_message.h @@ -113,7 +113,7 @@ public: const QRect &textRect); private: - static QVector CountLineWidths( + static std::vector CountLineWidths( const Ui::Text::String &text, const QRect &textRect); diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.cpp b/Telegram/SourceFiles/history/view/media/history_view_game.cpp index 788b28e2c..4767bfcce 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_game.cpp @@ -257,7 +257,7 @@ void Game::draw(Painter &p, const PaintContext &context) const { .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler), .selection = toDescriptionSelection(context.selection), - .elisionLines = _descriptionLines, + .elisionHeight = _descriptionLines * lineHeight, .elisionRemoveFromEnd = endskip, }); tshift += _descriptionLines * lineHeight; diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 4fb1cef59..454475369 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -583,7 +583,9 @@ void WebPage::draw(Painter &p, const PaintContext &context) const { .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler), .selection = toDescriptionSelection(context.selection), - .elisionLines = std::max(_descriptionLines, 0), + .elisionHeight = ((_descriptionLines > 0) + ? (_descriptionLines * lineHeight) + : 0), .elisionRemoveFromEnd = (_descriptionLines > 0) ? endskip : 0, }); tshift += (_descriptionLines > 0) diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index eae3b777f..c9e6d1050 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -524,7 +524,7 @@ void Cover::refreshStatusText() { _refreshStatusTimer.callOnce(updateIn); } return showOnline - ? PlainLink(result) + ? Ui::Text::Colorized(result) : TextWithEntities{ .text = result }; } else if (auto chat = _peer->asChat()) { if (!chat->amIn()) { @@ -543,7 +543,7 @@ void Cover::refreshStatusText() { onlineCount, channel->isMegagroup()); return hasMembersLink - ? PlainLink(result) + ? Ui::Text::Colorized(result) : TextWithEntities{ .text = result }; } return tr::lng_chat_status_unaccessible(tr::now, WithEntities); diff --git a/Telegram/SourceFiles/lang/lang_instance.cpp b/Telegram/SourceFiles/lang/lang_instance.cpp index e3fb5166b..e2d26b635 100644 --- a/Telegram/SourceFiles/lang/lang_instance.cpp +++ b/Telegram/SourceFiles/lang/lang_instance.cpp @@ -133,8 +133,8 @@ bool ValueParser::readTag() { _tagsUsed.insert(_currentTagIndex); if (_currentTagReplacer.isEmpty()) { - _currentTagReplacer = QString(4, TextCommand); - _currentTagReplacer[1] = kTextCommandLangTag; + _currentTagReplacer = QString(4, QChar(kTextCommand)); + _currentTagReplacer[1] = QChar(kTextCommandLangTag); } _currentTagReplacer[2] = QChar(0x0020 + _currentTagIndex); @@ -168,8 +168,10 @@ QString PrepareTestValue(const QString ¤t, QChar filler) { auto result = QString(size + 1, filler); auto inCommand = false; for (auto i = 0; i != size; ++i) { - auto ch = current[i]; - auto newInCommand = (ch.unicode() == TextCommand) ? (!inCommand) : inCommand; + const auto ch = current[i]; + const auto newInCommand = (ch.unicode() == kTextCommand) + ? (!inCommand) + : inCommand; if (inCommand || newInCommand || ch.isSpace()) { result[i + 1] = ch; } diff --git a/Telegram/SourceFiles/lang/lang_tag.cpp b/Telegram/SourceFiles/lang/lang_tag.cpp index 2b0f30544..71c7c8398 100644 --- a/Telegram/SourceFiles/lang/lang_tag.cpp +++ b/Telegram/SourceFiles/lang/lang_tag.cpp @@ -868,8 +868,10 @@ auto ChoosePlural = ChoosePluralDefault; int FindTagReplacementPosition(const QString &original, ushort tag) { for (auto s = original.constData(), ch = s, e = ch + original.size(); ch != e;) { - if (*ch == TextCommand) { - if (ch + kTagReplacementSize <= e && (ch + 1)->unicode() == kTextCommandLangTag && *(ch + 3) == TextCommand) { + if (ch->unicode() == kTextCommand) { + if (ch + kTagReplacementSize <= e + && (ch + 1)->unicode() == kTextCommandLangTag + && (ch + 3)->unicode() == kTextCommand) { if ((ch + 2)->unicode() == 0x0020 + tag) { return ch - s; } else { diff --git a/Telegram/SourceFiles/lang/lang_tag.h b/Telegram/SourceFiles/lang/lang_tag.h index 2efa0882c..297ae1ff2 100644 --- a/Telegram/SourceFiles/lang/lang_tag.h +++ b/Telegram/SourceFiles/lang/lang_tag.h @@ -11,11 +11,12 @@ enum lngtag_count : int; namespace Lang { +inline constexpr auto kTextCommand = 0x10; inline constexpr auto kTextCommandLangTag = 0x20; constexpr auto kTagReplacementSize = 4; [[nodiscard]] int FindTagReplacementPosition( - const QString &original, + const QString &original, ushort tag); struct ShortenedCount { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 8b92b92ab..779fe16d7 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -5014,7 +5014,7 @@ void OverlayWidget::paintCaptionContent( .spoiler = Ui::Text::DefaultSpoilerCache(), .pausedEmoji = On(PowerSaving::kEmojiChat), .pausedSpoiler = On(PowerSaving::kChatSpoiler), - .elisionLines = inner.height() / lineHeight, + .elisionHeight = inner.height(), .elisionRemoveFromEnd = _captionSkipBlockWidth, }); diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.cpp b/Telegram/SourceFiles/payments/ui/payments_panel.cpp index cb36c5887..ec436f9e3 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_panel.cpp @@ -888,17 +888,13 @@ void Panel::showWebviewError( rich.append("\n\n"); switch (information.error) { case Error::NoWebview2: { - const auto command = QString(QChar(TextCommand)); - const auto text = tr::lng_payments_webview_install_edge( + rich.append(tr::lng_payments_webview_install_edge( tr::now, lt_link, - command); - const auto parts = text.split(command); - rich.append(parts.value(0)) - .append(Text::Link( + Text::Link( "Microsoft Edge WebView2 Runtime", - "https://go.microsoft.com/fwlink/p/?LinkId=2124703")) - .append(parts.value(1)); + "https://go.microsoft.com/fwlink/p/?LinkId=2124703"), + Ui::Text::WithEntities)); } break; case Error::NoWebKitGTK: rich.append(tr::lng_payments_webview_install_webkit(tr::now)); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index 96546ae00..9b9f82215 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -1265,17 +1265,13 @@ void Panel::showWebviewError( rich.append("\n\n"); switch (information.error) { case Error::NoWebview2: { - const auto command = QString(QChar(TextCommand)); - const auto text = tr::lng_payments_webview_install_edge( + rich.append(tr::lng_payments_webview_install_edge( tr::now, lt_link, - command); - const auto parts = text.split(command); - rich.append(parts.value(0)) - .append(Text::Link( + Text::Link( "Microsoft Edge WebView2 Runtime", - "https://go.microsoft.com/fwlink/p/?LinkId=2124703")) - .append(parts.value(1)); + "https://go.microsoft.com/fwlink/p/?LinkId=2124703"), + Ui::Text::WithEntities)); } break; case Error::NoWebKitGTK: rich.append(tr::lng_payments_webview_install_webkit(tr::now)); diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index 97529516f..bb93cf9e8 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -469,9 +469,9 @@ void ChatStyle::assignPalette(not_null palette) { } for (auto &stm : _messageStyles) { - const auto same = (stm.textPalette.linkFg->c == stm.historyTextFg->c); - stm.textPalette.linkAlwaysActive = same ? 1 : 0; - stm.semiboldPalette.linkAlwaysActive = same ? 1 : 0; + stm.textPalette.linkAlwaysActive + = stm.semiboldPalette.linkAlwaysActive + = (stm.textPalette.linkFg->c == stm.historyTextFg->c); } _paletteChanged.fire({}); diff --git a/Telegram/SourceFiles/ui/chat/message_bar.cpp b/Telegram/SourceFiles/ui/chat/message_bar.cpp index 722e8322a..cde160d54 100644 --- a/Telegram/SourceFiles/ui/chat/message_bar.cpp +++ b/Telegram/SourceFiles/ui/chat/message_bar.cpp @@ -457,7 +457,7 @@ void MessageBar::paint(Painter &p) { .now = now, .pausedEmoji = paused || On(PowerSaving::kEmojiChat), .pausedSpoiler = pausedSpoiler, - .elisionLines = 1, + .elisionOneLine = true, }); } } else if (_animation->bodyAnimation == BodyAnimation::Text) { diff --git a/Telegram/SourceFiles/ui/empty_userpic.cpp b/Telegram/SourceFiles/ui/empty_userpic.cpp index 594fe6707..a319c3454 100644 --- a/Telegram/SourceFiles/ui/empty_userpic.cpp +++ b/Telegram/SourceFiles/ui/empty_userpic.cpp @@ -444,7 +444,7 @@ void EmptyUserpic::fillString(const QString &name) { } } else if (!letterFound && ch->isLetterOrNumber()) { letterFound = true; - if (ch + 1 != end && Ui::Text::IsDiac(*(ch + 1))) { + if (ch + 1 != end && Ui::Text::IsDiacritic(*(ch + 1))) { letters.push_back(QString(ch, 2)); levels.push_back(level); ++ch; diff --git a/Telegram/SourceFiles/ui/text/text_options.cpp b/Telegram/SourceFiles/ui/text/text_options.cpp index 382314370..20e66737e 100644 --- a/Telegram/SourceFiles/ui/text/text_options.cpp +++ b/Telegram/SourceFiles/ui/text/text_options.cpp @@ -76,7 +76,7 @@ TextParseOptions TextNameOptions = { }; TextParseOptions TextDialogOptions = { - TextParsePlainLinks | TextParseMarkdown, // flags + TextParseColorized | TextParseMarkdown, // flags 0, // maxw is style-dependent 1, // maxh Qt::LayoutDirectionAuto, // lang-dependent diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index b72cb48dd..4454587d2 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -828,7 +828,7 @@ void Notification::paintTitle(Painter &p) { .spoiler = Ui::Text::DefaultSpoilerCache(), .pausedEmoji = On(PowerSaving::kEmojiChat), .pausedSpoiler = On(PowerSaving::kChatSpoiler), - .elisionLines = 1, + .elisionOneLine = true, }); } @@ -842,7 +842,7 @@ void Notification::paintText(Painter &p) { .spoiler = Ui::Text::DefaultSpoilerCache(), .pausedEmoji = On(PowerSaving::kEmojiChat), .pausedSpoiler = On(PowerSaving::kChatSpoiler), - .elisionLines = _textRect.height() / st::dialogsTextFont->height, + .elisionHeight = _textRect.height(), }); } @@ -922,7 +922,7 @@ void Notification::updateNotifyDisplay() { 2 * st::dialogsTextFont->height); const auto text = !_reaction.empty() ? (!_author.isEmpty() - ? Ui::Text::PlainLink(_author).append(' ') + ? Ui::Text::Colorized(_author).append(' ') : TextWithEntities() ).append(Manager::ComposeReactionNotification( _item, @@ -935,7 +935,7 @@ void Notification::updateNotifyDisplay() { .spoilerLoginCode = options.spoilerLoginCode, }).text : ((!_author.isEmpty() - ? Ui::Text::PlainLink(_author) + ? Ui::Text::Colorized(_author) : TextWithEntities() ).append(_forwardedCount > 1 ? ('\n' + tr::lng_forward_messages( @@ -944,7 +944,7 @@ void Notification::updateNotifyDisplay() { _forwardedCount)) : QString())); const auto options = TextParseOptions{ - (TextParsePlainLinks + (TextParseColorized | TextParseMarkdown | (_forwardedCount > 1 ? TextParseMultiline : 0)), 0, diff --git a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp index 946649e12..82698486b 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_preview.cpp @@ -47,7 +47,7 @@ namespace { } } else if (!letterFound && ch->isLetterOrNumber()) { letterFound = true; - if (ch + 1 != end && Ui::Text::IsDiac(*(ch + 1))) { + if (ch + 1 != end && Ui::Text::IsDiacritic(*(ch + 1))) { letters.push_back(QString(ch, 2)); levels.push_back(level); ++ch; @@ -353,25 +353,25 @@ void Generator::generateData() { "Mike Apple", 2, "9:00", - Ui::Text::PlainLink(QChar(55357) + Ui::Text::Colorized(QChar(55357) + QString() + QChar(56836) + " Sticker")); _rows.back().unreadCounter = 2; _rows.back().muted = true; - addRow("Evening Club", 1, "8:00", Ui::Text::PlainLink("Eva: Photo")); + addRow("Evening Club", 1, "8:00", Ui::Text::Colorized("Eva: Photo")); _rows.back().type = Row::Type::Group; addRow( "Old Pirates", 6, "7:00", - Ui::Text::PlainLink("Max:").append(" Yo-ho-ho!")); + Ui::Text::Colorized("Max:").append(" Yo-ho-ho!")); _rows.back().type = Row::Type::Group; addRow("Max Bright", 3, "6:00", { .text = "How about some coffee?" }); _rows.back().status = Status::Received; addRow("Natalie Parker", 4, "5:00", { .text = "OK, great)" }); _rows.back().status = Status::Received; - addRow("Davy Jones", 5, "4:00", Ui::Text::PlainLink("Keynote.pdf")); + addRow("Davy Jones", 5, "4:00", Ui::Text::Colorized("Keynote.pdf")); _topBarName.setText(st::msgNameStyle, "Eva Summer", Ui::NameTextOptions()); _topBarStatus = "online"; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 93458e4cb..02440524e 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 93458e4cb3845c9743bb0c0a78f1e6ce7ee99363 +Subproject commit 02440524eaa2b1bd6d1909ff4aa2ca207a282b2c From da768ac1d1c3e5b2358669ba3b300deba8bf59d6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 4 Oct 2023 16:23:19 +0400 Subject: [PATCH 020/262] Add libprisma from Fela for syntax highlighting. --- .gitmodules | 3 +++ Telegram/CMakeLists.txt | 1 + Telegram/ThirdParty/libprisma | 1 + Telegram/build/prepare/prepare.py | 4 +++ Telegram/cmake/lib_prisma.cmake | 42 +++++++++++++++++++++++++++++++ cmake | 2 +- 6 files changed, 52 insertions(+), 1 deletion(-) create mode 160000 Telegram/ThirdParty/libprisma create mode 100644 Telegram/cmake/lib_prisma.cmake diff --git a/.gitmodules b/.gitmodules index b2af08731..23bbe74d5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -100,3 +100,6 @@ [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/CMakeLists.txt b/Telegram/CMakeLists.txt index d555578f7..01d67891c 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -28,6 +28,7 @@ include(cmake/lib_ffmpeg.cmake) include(cmake/lib_stripe.cmake) include(cmake/lib_tgvoip.cmake) include(cmake/lib_tgcalls.cmake) +include(cmake/lib_prisma.cmake) include(cmake/td_export.cmake) include(cmake/td_mtproto.cmake) include(cmake/td_lang.cmake) diff --git a/Telegram/ThirdParty/libprisma b/Telegram/ThirdParty/libprisma new file mode 160000 index 000000000..4521ec4da --- /dev/null +++ b/Telegram/ThirdParty/libprisma @@ -0,0 +1 @@ +Subproject commit 4521ec4dac39e64b66033de16b342d9f61d8b9c3 diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 33ae63dc3..f61a8b897 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -947,6 +947,10 @@ win: git checkout n11.1.5.1 """) +stage('regex', """ + git clone -b boost-1.83.0 https://github.com/boostorg/regex.git +""") + stage('ffmpeg', """ git clone https://github.com/FFmpeg/FFmpeg.git ffmpeg cd ffmpeg diff --git a/Telegram/cmake/lib_prisma.cmake b/Telegram/cmake/lib_prisma.cmake new file mode 100644 index 000000000..f23e1a219 --- /dev/null +++ b/Telegram/cmake/lib_prisma.cmake @@ -0,0 +1,42 @@ +# This file is part of Telegram Desktop, +# the official desktop application for the Telegram messaging service. +# +# For license and copyright information please follow this link: +# https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL + +add_library(lib_prisma STATIC) +init_target(lib_prisma) + +add_library(desktop-app::lib_prisma ALIAS lib_prisma) + +set(prisma_loc ${third_party_loc}/libprisma/libprisma) + +nice_target_sources(lib_prisma ${prisma_loc} +PRIVATE + Highlight.cpp + Highlight.h + LanguageTree.cpp + LanguageTree.h + SyntaxHighlighter.cpp + SyntaxHighlighter.h + TokenList.cpp + TokenList.h +) + +target_compile_definitions(lib_prisma +PRIVATE + BOOST_NO_INTRINSIC_WCHAR_T + BOOST_REGEX_NO_W32 +) + +target_include_directories(lib_prisma +PRIVATE + ${libs_loc}/regex/include +PUBLIC + ${prisma_loc} +) + +target_link_libraries(lib_prisma +PRIVATE + desktop-app::external_boost_regex +) diff --git a/cmake b/cmake index b1b0e95b0..813aacf79 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit b1b0e95b091f298c87cb9ec4458f426574221ca4 +Subproject commit 813aacf791af9bffcf740df2bd776d4b322e31f7 From 2414e927bd2e4fa313932d14fa285bb91550b666 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 3 Oct 2023 18:22:59 +0400 Subject: [PATCH 021/262] Add initial code syntax highlighting. Thanks PrismJS and Fela for porting it to C++. --- Telegram/CMakeLists.txt | 1 + Telegram/SourceFiles/data/data_session.cpp | 34 ++++++++++++++++++ Telegram/SourceFiles/data/data_session.h | 6 ++++ Telegram/SourceFiles/data/data_types.h | 2 ++ Telegram/SourceFiles/history/history_item.cpp | 22 ++++++++++-- Telegram/SourceFiles/history/history_item.h | 4 ++- .../history/view/history_view_message.cpp | 1 + .../view/media/history_view_document.cpp | 1 + .../media/history_view_extended_preview.cpp | 1 + .../history/view/media/history_view_gif.cpp | 1 + .../history/view/media/history_view_photo.cpp | 1 + Telegram/SourceFiles/ui/chat/chat_style.cpp | 35 +++++++++++++++++++ Telegram/SourceFiles/ui/chat/chat_style.h | 4 +++ Telegram/lib_spellcheck | 2 +- Telegram/lib_ui | 2 +- 15 files changed, 112 insertions(+), 5 deletions(-) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 01d67891c..130aca8bc 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1540,6 +1540,7 @@ elseif (APPLE) PRE_LINK COMMAND mkdir -p $/../Resources COMMAND cp ${CMAKE_BINARY_DIR}/lib_ui.rcc $/../Resources + COMMAND cp ${CMAKE_BINARY_DIR}/lib_spellcheck.rcc $/../Resources ) if (NOT build_macstore) add_custom_command(TARGET Telegram diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 68ef1afa2..4eba210f4 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -80,6 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "base/call_delayed.h" #include "base/random.h" +#include "spellcheck/spellcheck_highlight_syntax.h" #include "styles/style_boxes.h" // st::backgroundSize namespace Data { @@ -300,6 +301,11 @@ Session::Session(not_null session) } }, _lifetime); + Spellchecker::HighlightReady( + ) | rpl::start_with_next([=](uint64 processId) { + highlightProcessDone(processId); + }, _lifetime); + subscribeForTopicRepliesLists(); crl::on_main(_session, [=] { @@ -1760,6 +1766,27 @@ void Session::requestItemTextRefresh(not_null item) { } } +void Session::registerHighlightProcess( + uint64 processId, + not_null item) { + Expects(item->inHighlightProcess()); + + const auto [i, ok] = _highlightings.emplace(processId, item); + + Ensures(ok); +} + +void Session::highlightProcessDone(uint64 processId) { + if (const auto done = _highlightings.take(processId)) { + for (const auto &[id, item] : _highlightings) { + if (item == *done) { + return; + } + } + (*done)->highlightProcessDone(); + } +} + void Session::requestUnreadReactionsAnimation(not_null item) { enumerateItemViews(item, [&](not_null view) { view->animateUnreadReactions(); @@ -2415,6 +2442,13 @@ void Session::unregisterMessage(not_null item) { Data::MessageUpdate::Flag::Destroyed); groups().unregisterMessage(item); removeDependencyMessage(item); + for (auto i = begin(_highlightings); i != end(_highlightings);) { + if (i->second == item) { + i = _highlightings.erase(i); + } else { + ++i; + } + } messagesListForInsert(peerId)->erase(itemId); if (!peerIsChannel(peerId) && IsServerMsgId(itemId)) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 9ba13d1a9..86cb08c6f 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -299,6 +299,10 @@ public: void notifyPinnedDialogsOrderUpdated(); [[nodiscard]] rpl::producer<> pinnedDialogsOrderUpdated() const; + void registerHighlightProcess( + uint64 processId, + not_null item); + void registerHeavyViewPart(not_null view); void unregisterHeavyViewPart(not_null view); void unloadHeavyViewParts( @@ -845,6 +849,7 @@ private: TimeId date); void setWallpapers(const QVector &data, uint64 hash); + void highlightProcessDone(uint64 processId); void checkPollsClosings(); @@ -955,6 +960,7 @@ private: std::unordered_map< FullStoryId, base::flat_set>> _storyItems; + base::flat_map> _highlightings; base::flat_set> _webpagesUpdated; base::flat_set> _gamesUpdated; diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index a296e6cf4..1c2823a4f 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -303,6 +303,8 @@ enum class MessageFlag : uint64 { FakeBotAbout = (1ULL << 36), StoryItem = (1ULL << 37), + + InHighlightProcess = (1ULL << 38), }; inline constexpr bool is_flag_type(MessageFlag) { return true; } using MessageFlags = base::flags; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 746be6038..f5d04718d 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -67,6 +67,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_web_page.h" #include "chat_helpers/stickers_gift_box_pack.h" #include "payments/payments_checkout_process.h" // CheckoutProcess::Start. +#include "spellcheck/spellcheck_highlight_syntax.h" #include "styles/style_dialogs.h" namespace { @@ -2871,15 +2872,32 @@ void HistoryItem::setText(const TextWithEntities &textWithEntities) { : std::move(textWithEntities)); } -void HistoryItem::setTextValue(TextWithEntities text) { +void HistoryItem::setTextValue(TextWithEntities text, bool force) { + if (const auto processId = Spellchecker::TryHighlightSyntax(text)) { + _flags |= MessageFlag::InHighlightProcess; + history()->owner().registerHighlightProcess(processId, this); + } const auto had = !_text.empty(); _text = std::move(text); RemoveComponents(HistoryMessageTranslation::Bit()); - if (had) { + if (had || force) { history()->owner().requestItemTextRefresh(this); } } +bool HistoryItem::inHighlightProcess() const { + return _flags & MessageFlag::InHighlightProcess; +} + +void HistoryItem::highlightProcessDone() { + Expects(inHighlightProcess()); + + _flags &= ~MessageFlag::InHighlightProcess; + if (!_text.empty()) { + setTextValue(base::take(_text), true); + } +} + bool HistoryItem::showNotification() const { const auto channel = _history->peer->asChannel(); if (channel && !channel->amIn()) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 7e54081dc..d47660393 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -322,6 +322,8 @@ public: [[nodiscard]] bool repliesAreComments() const; [[nodiscard]] bool externalReply() const; [[nodiscard]] bool hasExtendedMediaPreview() const; + [[nodiscard]] bool inHighlightProcess() const; + void highlightProcessDone(); void setCommentsInboxReadTill(MsgId readTillId); void setCommentsMaxId(MsgId maxId); @@ -537,7 +539,7 @@ private: [[nodiscard]] bool generateLocalEntitiesByReply() const; [[nodiscard]] TextWithEntities withLocalEntities( const TextWithEntities &textWithEntities) const; - void setTextValue(TextWithEntities text); + void setTextValue(TextWithEntities text, bool force = false); [[nodiscard]] bool isTooOldForEdit(TimeId now) const; [[nodiscard]] bool isLegacyMessage() const { return _flags & MessageFlag::Legacy; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index aa453c225..13d9efb5d 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -1622,6 +1622,7 @@ void Message::paintText( .position = trect.topLeft(), .availableWidth = trect.width(), .palette = &stm->textPalette, + .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index d1ddb2b96..5caedc79f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -748,6 +748,7 @@ void Document::draw( .position = { st::msgPadding.left(), captiontop }, .availableWidth = captionw, .palette = &stm->textPalette, + .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), diff --git a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp index f2768fef1..252b16a4d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_extended_preview.cpp @@ -235,6 +235,7 @@ void ExtendedPreview::draw(Painter &p, const PaintContext &context) const { painty + painth + st::mediaCaptionSkip), .availableWidth = captionw, .palette = &stm->textPalette, + .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index ba5a5bbef..d2ec6a551 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -709,6 +709,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const { .position = QPoint(st::msgPadding.left(), top), .availableWidth = captionw, .palette = &stm->textPalette, + .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index 0c8849699..034e52d6c 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -405,6 +405,7 @@ void Photo::draw(Painter &p, const PaintContext &context) const { .position = QPoint(st::msgPadding.left(), top), .availableWidth = captionw, .palette = &stm->textPalette, + .colors = context.st->highlightColors(), .spoiler = Ui::Text::DefaultSpoilerCache(), .now = context.now, .pausedEmoji = context.paused || On(PowerSaving::kEmojiChat), diff --git a/Telegram/SourceFiles/ui/chat/chat_style.cpp b/Telegram/SourceFiles/ui/chat/chat_style.cpp index bb93cf9e8..d188992ce 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_style.cpp @@ -446,6 +446,41 @@ void ChatStyle::applyAdjustedServiceBg(QColor serviceBg) { msgServiceBg().set(uchar(r), uchar(g), uchar(b), uchar(a)); } +std::span ChatStyle::highlightColors() const { + if (_highlightColors.empty()) { + const auto push = [&](const style::color &color) { + _highlightColors.push_back({ &color->p, &color->p }); + }; + + // comment, block-comment, prolog, doctype, cdata + push(statisticsChartLineLightblue()); + + // punctuation + push(statisticsChartLineRed()); + + // property, tag, boolean, number, + // constant, symbol, deleted + push(statisticsChartLineRed()); + + // selector, attr-name, string, char, builtin, inserted + push(statisticsChartLineOrange()); + + // operator, entity, url + push(statisticsChartLineRed()); + + // atrule, attr-value, keyword, function + push(statisticsChartLineBlue()); + + // class-name + push(statisticsChartLinePurple()); + + //push(statisticsChartLineLightgreen()); + //push(statisticsChartLineGreen()); + //push(statisticsChartLineGolden()); + } + return _highlightColors; +} + void ChatStyle::assignPalette(not_null palette) { *static_cast(this) = *palette; style::internal::resetIcons(); diff --git a/Telegram/SourceFiles/ui/chat/chat_style.h b/Telegram/SourceFiles/ui/chat/chat_style.h index f1fd0b807..cefe34a19 100644 --- a/Telegram/SourceFiles/ui/chat/chat_style.h +++ b/Telegram/SourceFiles/ui/chat/chat_style.h @@ -168,6 +168,8 @@ public: void applyCustomPalette(const style::palette *palette); void applyAdjustedServiceBg(QColor serviceBg); + [[nodiscard]] std::span highlightColors() const; + [[nodiscard]] rpl::producer<> paletteChanged() const { return _paletteChanged.events(); } @@ -332,6 +334,8 @@ private: mutable CornersPixmaps _msgSelectOverlayCorners[ int(CachedCornerRadius::kCount)]; + mutable std::vector _highlightColors; + style::TextPalette _historyPsaForwardPalette; style::TextPalette _imgReplyTextPalette; style::TextPalette _serviceTextPalette; diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index ab2a9d1c1..a9fb03101 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit ab2a9d1c19b948a05b216801f8ba4fea0759428a +Subproject commit a9fb0310132bc645ffcfb3734bd2ca53f1fb39fa diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 02440524e..0d8717d48 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 02440524eaa2b1bd6d1909ff4aa2ca207a282b2c +Subproject commit 0d8717d48ac3f2228399a1de78a28f0a7bbd0023 From 50097e1a819b199af70edbecaf0317fad706e70f Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 5 Oct 2023 10:35:31 +0400 Subject: [PATCH 022/262] Show spellcheck suggestions on Ctrl+Space. Fixes #26892. --- Telegram/lib_spellcheck | 2 +- Telegram/lib_ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index a9fb03101..f41bb5c80 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit a9fb0310132bc645ffcfb3734bd2ca53f1fb39fa +Subproject commit f41bb5c8038e02b2dde5af9ce40a633a49c2f49b diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 0d8717d48..3698eb538 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 0d8717d48ac3f2228399a1de78a28f0a7bbd0023 +Subproject commit 3698eb53827dc9929e583666a8e7f6462741d1a6 From b68a7c7f044c27858bfa04228824d1e672e8b8eb Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 6 Oct 2023 09:03:28 +0400 Subject: [PATCH 023/262] Update submodules. --- Telegram/lib_base | 2 +- Telegram/lib_webview | 2 +- cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index 0912ba4b3..ab69403ac 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 0912ba4b3b67fc485dc43f64664b5a56b9509e02 +Subproject commit ab69403ace74bcf0e15a1a2e149dac66993ecdcf diff --git a/Telegram/lib_webview b/Telegram/lib_webview index f1a829c56..72c6c11c0 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit f1a829c569f3e3bc8189d7411373a9bed6877bd2 +Subproject commit 72c6c11c05863bafe20bb4495907aacd7224d3b5 diff --git a/cmake b/cmake index 813aacf79..78a86e6fe 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 813aacf791af9bffcf740df2bd776d4b322e31f7 +Subproject commit 78a86e6feea398ece7b5c89869673b169ade2454 From 92fadd265290863b77749af789d97c7ed42b710b Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 6 Oct 2023 14:43:42 +0400 Subject: [PATCH 024/262] Fix build with GCC 12. --- Telegram/ThirdParty/libprisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/ThirdParty/libprisma b/Telegram/ThirdParty/libprisma index 4521ec4da..b9a1ed1a1 160000 --- a/Telegram/ThirdParty/libprisma +++ b/Telegram/ThirdParty/libprisma @@ -1 +1 @@ -Subproject commit 4521ec4dac39e64b66033de16b342d9f61d8b9c3 +Subproject commit b9a1ed1a1918b700eb3d140f5047f4f7533421c2 From 14f68c2f33c8df554bffc66b0411004cc982c0da Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 6 Oct 2023 17:49:08 +0400 Subject: [PATCH 025/262] Fix webview on Windows & macOS. --- Telegram/lib_webview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_webview b/Telegram/lib_webview index 72c6c11c0..d839bbed3 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit 72c6c11c05863bafe20bb4495907aacd7224d3b5 +Subproject commit d839bbed3fe61219dc9d857108fe5ac74105dbc7 From bf3f474195999f76df10eb9224710beba13aaba3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 6 Oct 2023 17:02:59 +0400 Subject: [PATCH 026/262] Fix label position in peer editing. --- Telegram/SourceFiles/info/info.style | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style index ffd146b89..7ac6d4a1b 100644 --- a/Telegram/SourceFiles/info/info.style +++ b/Telegram/SourceFiles/info/info.style @@ -582,12 +582,11 @@ manageGroupButtonInner: SettingsButton(infoProfileButton) { } manageGroupButton: SettingsCountButton(managePeerButton) { button: manageGroupButtonInner; - labelPosition: point(22px, 12px); + labelPosition: point(22px, 10px); iconPosition: point(20px, 4px); } manageGroupTopButtonWithText: SettingsCountButton(manageGroupButton) { - labelPosition: point(22px, 10px); iconPosition: point(0px, 0px); } manageGroupTopicsButton: SettingsCountButton(manageGroupTopButtonWithText) { From a5ec616382dd0034d62bccd241ad536242bd9b0b Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 6 Oct 2023 14:46:32 +0400 Subject: [PATCH 027/262] Downgrade qtsvg to 6.5.3 in snap As a better crash workaround --- snap/snapcraft.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 465d3f4b0..c7eb6984d 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -468,6 +468,9 @@ parts: cd qtbase find $CRAFT_STAGE/patches/qtbase_${QT} -type f -print0 | sort -z | xargs -r0 git apply + cd ../qtsvg + git fetch origin v6.5.3 + git checkout FETCH_HEAD cd ../qtwayland sed -i 's/qMin(version, 8)/qMin(version, 7)/' src/client/qwaylandinputdevice.cpp cd .. @@ -501,7 +504,6 @@ parts: - -./usr/lib/$CRAFT_ARCH_TRIPLET/*.la - -./usr/lib/$CRAFT_ARCH_TRIPLET/*.prl - -./usr/lib/$CRAFT_ARCH_TRIPLET/*.so - - -./usr/plugins/imageformats/libqsvg.so - -./usr/libexec - -./usr/mkspecs - -./usr/modules From 93fbad50bc154754e1aa3b4518c907af3e02c795 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 6 Oct 2023 14:20:24 +0400 Subject: [PATCH 028/262] Downgrade qtwayland to 6.5.3 --- Telegram/build/docker/centos_env/Dockerfile | 5 +++++ snap/snapcraft.yaml | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 3194803e8..49001895e 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -757,6 +757,11 @@ RUN git clone -b {{ QT_TAG }} --depth=1 https://code.qt.io/qt/qt5.git qt_{{ QT } && git submodule update --init --recursive --depth=1 qtbase qtdeclarative qtwayland qtimageformats qtsvg qtshadertools \ && cd qtbase \ && find ../../patches/qtbase_{{ QT }} -type f -print0 | sort -z | xargs -r0 git apply \ + && cd ../qtwayland \ + && git fetch origin v6.5.3 \ + && git checkout FETCH_HEAD \ + && git fetch origin da4bb4885d91fad53a573bbeacf1b0705eb5b1a5 \ + && git cherry-pick -n FETCH_HEAD \ && cd .. \ && ./configure -prefix "{{ QT_PREFIX }}" \ CMAKE_BUILD_TYPE=None \ diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index c7eb6984d..a3df97c34 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -472,6 +472,10 @@ parts: git fetch origin v6.5.3 git checkout FETCH_HEAD cd ../qtwayland + git fetch origin v6.5.3 + git checkout FETCH_HEAD + git fetch origin da4bb4885d91fad53a573bbeacf1b0705eb5b1a5 + git cherry-pick -n FETCH_HEAD sed -i 's/qMin(version, 8)/qMin(version, 7)/' src/client/qwaylandinputdevice.cpp cd .. override-build: | From 6bc0179919b781cc08543e5bf1f7b531444da3b2 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 6 Oct 2023 23:08:11 +0400 Subject: [PATCH 029/262] Work with GLIB_VERSION_MAX_ALLOWED --- .../SourceFiles/platform/linux/notifications_manager_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index eddd32dc8..c94400ff1 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -415,7 +415,7 @@ bool NotificationData::init( static const auto set_category = [] { // reset dlerror after dlsym call const auto guard = gsl::finally([] { dlerror(); }); - return reinterpret_cast( + return reinterpret_cast( dlsym(RTLD_DEFAULT, "g_notification_set_category")); }(); From aadaf475699d90b05e2fb7a70de0498d92d7e462 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 6 Oct 2023 23:29:40 +0400 Subject: [PATCH 030/262] Add boost-regex to snap --- snap/snapcraft.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a3df97c34..d850c04dd 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -102,7 +102,8 @@ parts: - python3 - libasound2-dev - libavif-dev - - libboost-program-options-dev + - libboost-program-options1.74-dev + - libboost-regex1.74-dev - libfmt-dev - libgirepository1.0-dev - libheif-dev @@ -118,6 +119,7 @@ parts: stage-packages: - libasound2 - libavif13 + - libboost-regex1.74.0 - libheif1 - libopus0 - libpulse0 From ac699ccf80afe732c7177082644eb548269c7c0e Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 6 Oct 2023 16:02:18 +0400 Subject: [PATCH 031/262] Update submodules --- Telegram/lib_base | 2 +- Telegram/lib_webview | 2 +- cmake | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index ab69403ac..bc7b5bd4a 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit ab69403ace74bcf0e15a1a2e149dac66993ecdcf +Subproject commit bc7b5bd4a23f57fd09ea573b20b4b63cd1fe0bf2 diff --git a/Telegram/lib_webview b/Telegram/lib_webview index d839bbed3..fc89a7bbe 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit d839bbed3fe61219dc9d857108fe5ac74105dbc7 +Subproject commit fc89a7bbead3ecfddac8ff4429a2268c12bfc0d6 diff --git a/cmake b/cmake index 78a86e6fe..0df256ce9 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 78a86e6feea398ece7b5c89869673b169ade2454 +Subproject commit 0df256ce99e1de129a6f7927766f8824b6bb158e From 501784cd15da0d41bffccda00c0f594ea4a85919 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 8 Oct 2023 07:02:40 +0400 Subject: [PATCH 032/262] Attempt to fix Snap build. --- Telegram/ThirdParty/libprisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/ThirdParty/libprisma b/Telegram/ThirdParty/libprisma index b9a1ed1a1..16954ab5e 160000 --- a/Telegram/ThirdParty/libprisma +++ b/Telegram/ThirdParty/libprisma @@ -1 +1 @@ -Subproject commit b9a1ed1a1918b700eb3d140f5047f4f7533421c2 +Subproject commit 16954ab5e992b653f96e567d0cc22660cb64ab98 From 41dada2c06a00e4a1c848a82e55a2c5bedc17515 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 3 Oct 2023 19:01:39 +0300 Subject: [PATCH 033/262] Fixed shadows in userpic builder with non-default scale. --- .../info/userpic/info_userpic_bubble_wrap.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/info/userpic/info_userpic_bubble_wrap.cpp b/Telegram/SourceFiles/info/userpic/info_userpic_bubble_wrap.cpp index 95f383b1e..ffb0204fc 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_bubble_wrap.cpp +++ b/Telegram/SourceFiles/info/userpic/info_userpic_bubble_wrap.cpp @@ -18,22 +18,26 @@ namespace Ui { namespace { void PaintExcludeTopShadow(QPainter &p, int radius, const QRect &r) { - constexpr auto kHorizontalOffset = 1; - constexpr auto kVerticalOffset = 2; + constexpr auto kHorizontalOffset = 1.; + constexpr auto kVerticalOffset = 2.; + constexpr auto kOpacityStep1 = 0.2; + constexpr auto kOpacityStep2 = 0.4; const auto opacity = p.opacity(); - p.setOpacity(opacity * 0.2); + const auto hOffset = style::ConvertScale(kHorizontalOffset); + const auto vOffset = style::ConvertScale(kVerticalOffset); + p.setOpacity(opacity * kOpacityStep1); p.drawRoundedRect( - r + QMargins(kHorizontalOffset, -radius, kHorizontalOffset, 0), + r + QMarginsF(hOffset, -radius, hOffset, 0), radius, radius); - p.setOpacity(opacity * 0.2); + p.setOpacity(opacity * kOpacityStep1); p.drawRoundedRect( - r + QMargins(0, 0, 0, kVerticalOffset), + r + QMarginsF(0, 0, 0, vOffset), radius, radius); - p.setOpacity(opacity * 0.4); + p.setOpacity(opacity * kOpacityStep2); p.drawRoundedRect( - r + QMargins(0, 0, 0, kVerticalOffset / 2), + r + QMarginsF(0, 0, 0, vOffset / 2.), radius, radius); p.setOpacity(opacity); From a7ca15657bf61597e1efe2475c188c0fe0f5c398 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 11 Oct 2023 06:04:59 +0300 Subject: [PATCH 034/262] Fixed master branch updater Github Action. --- .github/workflows/master_updater.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master_updater.yml b/.github/workflows/master_updater.yml index dc77d50d0..284bb844e 100644 --- a/.github/workflows/master_updater.yml +++ b/.github/workflows/master_updater.yml @@ -11,7 +11,9 @@ jobs: SKIP: "0" to_branch: "master" steps: - - uses: actions/checkout@v3.1.0 + - uses: actions/checkout@v4.1.0 + with: + fetch-depth: 0 if: env.SKIP == '0' - name: Push the code to the master branch. if: env.SKIP == '0' From bfe272e39ff044fd4b2d6e655b9fbf82f4b9b372 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 9 Oct 2023 10:06:01 +0400 Subject: [PATCH 035/262] Fix highlighting of the closing bracket. --- Telegram/lib_spellcheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index f41bb5c80..5227bec65 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit f41bb5c8038e02b2dde5af9ce40a633a49c2f49b +Subproject commit 5227bec65b6cb8c2b3172747ecb5f3468580de10 From aa7575dec44f8d99cc9635385f17055018d64298 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 10 Oct 2023 15:31:26 +0400 Subject: [PATCH 036/262] Highlight more languages. --- Telegram/ThirdParty/libprisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/ThirdParty/libprisma b/Telegram/ThirdParty/libprisma index 16954ab5e..7f62494bb 160000 --- a/Telegram/ThirdParty/libprisma +++ b/Telegram/ThirdParty/libprisma @@ -1 +1 @@ -Subproject commit 16954ab5e992b653f96e567d0cc22660cb64ab98 +Subproject commit 7f62494bb8cb6478a7cb6053ad7b5668fa013366 From 9116328f29e56cfcd1297ba11ee9d96d1c934e13 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 10 Oct 2023 23:03:39 +0400 Subject: [PATCH 037/262] Update to Qt 6.6.0 release on Linux --- Telegram/build/docker/centos_env/Dockerfile | 4 ++-- snap/snapcraft.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 49001895e..5f262ed66 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -1,7 +1,7 @@ {%- set GIT = "https://github.com" -%} {%- set GIT_FREEDESKTOP = GIT ~ "/gitlab-freedesktop-mirrors" -%} {%- set QT = "6.6.0" -%} -{%- set QT_TAG = "v" ~ QT ~ "-rc1" -%} +{%- set QT_TAG = "v" ~ QT -%} {%- set QT_PREFIX = "/usr/local/desktop-app/Qt-" ~ QT -%} {%- set OPENSSL_VER = "1_1_1" -%} {%- set OPENSSL_PREFIX = "/usr/local/desktop-app/openssl-1.1.1" -%} @@ -54,7 +54,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 413a12468bfdd02b9f1372c5c139753b0c5a432e \ + && git fetch --depth=1 origin c80c0d530200e3f27a9a39b4f230919529e73bd2 \ && git reset --hard FETCH_HEAD \ && rm -rf .git diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index d850c04dd..feae5eb22 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -465,7 +465,7 @@ parts: override-pull: | QT=6.6.0 - git clone -b v${QT}-rc1 --depth=1 https://code.qt.io/qt/qt5.git . + git clone -b v${QT} --depth=1 https://code.qt.io/qt/qt5.git . git submodule update --init --recursive --depth=1 qtbase qtdeclarative qtwayland qtimageformats qtsvg qtshadertools cd qtbase From 830fb3ccc223acc75691252b3b70ce2922a9c0e0 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 11 Oct 2023 21:38:14 +0400 Subject: [PATCH 038/262] Update submodules --- Telegram/lib_webview | 2 +- cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/lib_webview b/Telegram/lib_webview index fc89a7bbe..f5fe3afcf 160000 --- a/Telegram/lib_webview +++ b/Telegram/lib_webview @@ -1 +1 @@ -Subproject commit fc89a7bbead3ecfddac8ff4429a2268c12bfc0d6 +Subproject commit f5fe3afcf5bc5edf07a78ad18b9e8d33c3fbfdb2 diff --git a/cmake b/cmake index 0df256ce9..21a8d7996 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 0df256ce99e1de129a6f7927766f8824b6bb158e +Subproject commit 21a8d7996d50d973976607360f2f563f38af1c29 From f457a9d109452a040938cf8f786d3c711b7a3747 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 11 Oct 2023 21:06:23 +0300 Subject: [PATCH 039/262] Added ability to fast change forward options with right click on panel. --- .../SourceFiles/history/history_widget.cpp | 6 +- .../controls/history_view_forward_panel.cpp | 79 +++++++++++++------ .../controls/history_view_forward_panel.h | 1 + 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 8e2f08809..82390ece5 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -6195,7 +6195,11 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) { crl::guard(_list, [=] { cancelEdit(); })); } else if (_inReplyEditForward) { if (isReadyToForward) { - _forwardPanel->editOptions(controller()->uiShow()); + if (e->button() != Qt::LeftButton) { + _forwardPanel->editToNextOption(); + } else { + _forwardPanel->editOptions(controller()->uiShow()); + } } else { controller()->showPeerHistory( _peer, diff --git a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp index 136d52712..9ed8bc49a 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.cpp @@ -36,6 +36,31 @@ constexpr auto kUnknownVersion = -1; constexpr auto kNameWithCaptionsVersion = -2; constexpr auto kNameNoCaptionsVersion = -3; +[[nodiscard]] bool HasCaptions(const HistoryItemsList &list) { + for (const auto &item : list) { + if (const auto media = item->media()) { + if (!item->originalText().text.isEmpty() + && media->allowsEditCaption()) { + return true; + } + } + } + return false; +} + +[[nodiscard]] bool HasOnlyForcedForwardedInfo(const HistoryItemsList &list) { + for (const auto &item : list) { + if (const auto media = item->media()) { + if (!media->forceForwardedInfo()) { + return false; + } + } else { + return false; + } + } + return true; +} + } // namespace ForwardPanel::ForwardPanel(Fn repaint) @@ -224,32 +249,10 @@ void ForwardPanel::editOptions(std::shared_ptr show) { const auto now = _data.options; const auto count = _data.items.size(); const auto dropNames = (now != Options::PreserveInfo); - const auto hasCaptions = [&] { - for (const auto item : _data.items) { - if (const auto media = item->media()) { - if (!item->originalText().text.isEmpty() - && media->allowsEditCaption()) { - return true; - } - } - } - return false; - }(); - const auto hasOnlyForcedForwardedInfo = [&] { - if (hasCaptions) { - return false; - } - for (const auto item : _data.items) { - if (const auto media = item->media()) { - if (!media->forceForwardedInfo()) { - return false; - } - } else { - return false; - } - } - return true; - }(); + const auto hasCaptions = HasCaptions(_data.items); + const auto hasOnlyForcedForwardedInfo = hasCaptions + ? false + : HasOnlyForcedForwardedInfo(_data.items); const auto dropCaptions = (now == Options::NoNamesAndCaptions); const auto weak = base::make_weak(this); const auto changeRecipient = crl::guard(this, [=] { @@ -299,6 +302,30 @@ void ForwardPanel::editOptions(std::shared_ptr show) { changeRecipient)); } +void ForwardPanel::editToNextOption() { + using Options = Data::ForwardOptions; + const auto hasCaptions = HasCaptions(_data.items); + const auto hasOnlyForcedForwardedInfo = hasCaptions + ? false + : HasOnlyForcedForwardedInfo(_data.items); + if (hasOnlyForcedForwardedInfo) { + return; + } + + const auto now = _data.options; + const auto next = (now == Options::PreserveInfo) + ? Options::NoSenderNames + : ((now == Options::NoSenderNames) && hasCaptions) + ? Options::NoNamesAndCaptions + : Options::PreserveInfo; + + _to->owningHistory()->setForwardDraft(_to->topicRootId(), { + .ids = _to->owner().itemsToIds(_data.items), + .options = next, + }); + _repaint(); +} + void ForwardPanel::paint( Painter &p, int x, diff --git a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h index b178e27d0..038a0fbf7 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_forward_panel.h @@ -47,6 +47,7 @@ public: [[nodiscard]] rpl::producer<> itemsUpdated() const; void editOptions(std::shared_ptr show); + void editToNextOption(); [[nodiscard]] const HistoryItemsList &items() const; [[nodiscard]] bool empty() const; From ad8f8513c3b40f2e59b3e5e28c5722425fc69cc8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 11 Oct 2023 22:04:47 +0400 Subject: [PATCH 040/262] Link crashpad_handler with Xcode bug workaround. --- Telegram/build/prepare/prepare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index f61a8b897..ee3e77d9b 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1211,7 +1211,7 @@ stage('crashpad', """ mac: git clone https://github.com/desktop-app/crashpad.git cd crashpad - git checkout c1b7afa2fd + git checkout 171b601938 git submodule init git submodule update third_party/mini_chromium ZLIB_PATH=$USED_PREFIX/include From b0a65885c98f5b66e296d0416dc7508416d0b5f9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 26 Apr 2023 21:49:51 +0300 Subject: [PATCH 041/262] Added initial entry point for channel statistics. --- Telegram/Resources/langs/lang.strings | 2 ++ Telegram/SourceFiles/data/data_channel.h | 1 + Telegram/SourceFiles/window/window_peer_menu.cpp | 11 +++++++++++ 3 files changed, 14 insertions(+) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 35a4324ef..49a7da6b8 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4065,6 +4065,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_stories_link_invalid" = "This link is broken or has expired."; +"lng_stats_title" = "Statistics"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index ee0550a6d..bdf0f4205 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -62,6 +62,7 @@ enum class ChannelDataFlag { StoriesHidden = (1 << 26), HasActiveStories = (1 << 27), HasUnreadStories = (1 << 28), + CanGetStatistics = (1 << 29), }; inline constexpr bool is_flag_type(ChannelDataFlag) { return true; }; using ChannelDataFlags = base::flags; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index bef94d663..7727ecc9b 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -283,6 +283,7 @@ private: void addSearchTopics(); void addDeleteTopic(); void addVideoChat(); + void addViewStatistics(); not_null _controller; Dialogs::EntryState _request; @@ -997,6 +998,15 @@ void Filler::addManageChat() { }, &st::menuIconManage); } +void Filler::addViewStatistics() { + if (const auto channel = _peer->asChannel()) { + if (channel->flags() & ChannelDataFlag::CanGetStatistics) { + _addAction(tr::lng_stats_title(tr::now), [=] { + }, nullptr); + } + } +} + void Filler::addCreatePoll() { const auto can = _topic ? Data::CanSend(_topic, ChatRestriction::SendPolls) @@ -1237,6 +1247,7 @@ void Filler::fillProfileActions() { addGiftPremium(); addBotToGroup(); addNewMembers(); + addViewStatistics(); addStoryArchive(); addManageChat(); addTopicLink(); From 10968d0da2d7e5640315e1b6616af08b54c72465 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 26 Apr 2023 22:06:54 +0300 Subject: [PATCH 042/262] Added dummy box class for statistics. --- Telegram/CMakeLists.txt | 2 ++ .../SourceFiles/statistics/statistics_box.cpp | 19 +++++++++++++++++++ .../SourceFiles/statistics/statistics_box.h | 16 ++++++++++++++++ .../SourceFiles/window/window_peer_menu.cpp | 6 ++++++ 4 files changed, 43 insertions(+) create mode 100644 Telegram/SourceFiles/statistics/statistics_box.cpp create mode 100644 Telegram/SourceFiles/statistics/statistics_box.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 130aca8bc..8d488831d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1278,6 +1278,8 @@ PRIVATE settings/settings_type.h settings/settings_websites.cpp settings/settings_websites.h + statistics/statistics_box.cpp + statistics/statistics_box.h storage/details/storage_file_utilities.cpp storage/details/storage_file_utilities.h storage/details/storage_settings_scheme.cpp diff --git a/Telegram/SourceFiles/statistics/statistics_box.cpp b/Telegram/SourceFiles/statistics/statistics_box.cpp new file mode 100644 index 000000000..3c6457813 --- /dev/null +++ b/Telegram/SourceFiles/statistics/statistics_box.cpp @@ -0,0 +1,19 @@ +/* +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 "statistics/statistics_box.h" + +#include "data/data_peer.h" +#include "lang/lang_keys.h" +#include "ui/layers/generic_box.h" + +namespace { +} // namespace + +void StatisticsBox(not_null box, not_null peer) { + box->setTitle(tr::lng_stats_title()); +} diff --git a/Telegram/SourceFiles/statistics/statistics_box.h b/Telegram/SourceFiles/statistics/statistics_box.h new file mode 100644 index 000000000..5578cb3bc --- /dev/null +++ b/Telegram/SourceFiles/statistics/statistics_box.h @@ -0,0 +1,16 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class PeerData; + +namespace Ui { +class GenericBox; +} // namespace Ui + +void StatisticsBox(not_null box, not_null peer); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 7727ecc9b..052c2484f 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -85,6 +85,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "export/export_manager.h" #include "boxes/peers/edit_peer_info_box.h" +#include "statistics/statistics_box.h" #include "styles/style_chat.h" #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -999,12 +1000,17 @@ void Filler::addManageChat() { } void Filler::addViewStatistics() { +#ifdef _DEBUG if (const auto channel = _peer->asChannel()) { if (channel->flags() & ChannelDataFlag::CanGetStatistics) { + const auto navigation = _controller; + const auto peer = _peer; _addAction(tr::lng_stats_title(tr::now), [=] { + navigation->show(Box(StatisticsBox, peer)); }, nullptr); } } +#endif } void Filler::addCreatePoll() { From c45025c6e501dcc870b0fbcad46d203fb247bd4c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 26 Apr 2023 23:05:12 +0300 Subject: [PATCH 043/262] Added dummy API class for statistics. --- Telegram/CMakeLists.txt | 2 ++ Telegram/SourceFiles/api/api_statistics.cpp | 27 +++++++++++++++++++ Telegram/SourceFiles/api/api_statistics.h | 29 +++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 Telegram/SourceFiles/api/api_statistics.cpp create mode 100644 Telegram/SourceFiles/api/api_statistics.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 8d488831d..fef6f1492 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -152,6 +152,8 @@ PRIVATE api/api_sensitive_content.h api/api_single_message_search.cpp api/api_single_message_search.h + api/api_statistics.cpp + api/api_statistics.h api/api_text_entities.cpp api/api_text_entities.h api/api_toggling_media.cpp diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp new file mode 100644 index 000000000..839fa98fd --- /dev/null +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -0,0 +1,27 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "api/api_statistics.h" + +#include "apiwrap.h" +#include "data/data_peer.h" +#include "main/main_session.h" + +namespace Api { +namespace { +} // namespace + +Statistics::Statistics(not_null api) +: _api(&api->instance()) { +} + +rpl::producer<> Statistics::request( + not_null peer) const { + return rpl::never<>(); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_statistics.h b/Telegram/SourceFiles/api/api_statistics.h new file mode 100644 index 000000000..2197c9f82 --- /dev/null +++ b/Telegram/SourceFiles/api/api_statistics.h @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "mtproto/sender.h" + +class ApiWrap; +class PeerData; + +namespace Api { + +class Statistics final { +public: + explicit Statistics(not_null api); + + [[nodiscard]] rpl::producer<> request( + not_null peer) const; + +private: + MTP::Sender _api; + +}; + +} // namespace Api From ca863bfb5be32d1ea259b3ebb683fdb84a0ae3a3 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 26 Apr 2023 23:26:30 +0300 Subject: [PATCH 044/262] Added dummy Data class for statistics. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/data/data_statistics.cpp | 14 +++ Telegram/SourceFiles/data/data_statistics.h | 107 ++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 Telegram/SourceFiles/data/data_statistics.cpp create mode 100644 Telegram/SourceFiles/data/data_statistics.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index fef6f1492..ca2fccad8 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -555,6 +555,8 @@ PRIVATE data/data_sparse_ids.h data/data_sponsored_messages.cpp data/data_sponsored_messages.h + data/data_statistics.cpp + data/data_statistics.h data/data_stories.cpp data/data_stories.h data/data_stories_ids.cpp diff --git a/Telegram/SourceFiles/data/data_statistics.cpp b/Telegram/SourceFiles/data/data_statistics.cpp new file mode 100644 index 000000000..604797551 --- /dev/null +++ b/Telegram/SourceFiles/data/data_statistics.cpp @@ -0,0 +1,14 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "data/data_statistics.h" + +namespace Data { +namespace { +} // namespace + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_statistics.h b/Telegram/SourceFiles/data/data_statistics.h new file mode 100644 index 000000000..dd0c64071 --- /dev/null +++ b/Telegram/SourceFiles/data/data_statistics.h @@ -0,0 +1,107 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Data { + +struct StatisticsMessageInteractionInfo final { + MsgId messageId; + int viewsCount = 0; + int forwardsCount = 0; +}; + +struct StatisticsMessageSenderInfo final { + UserId userId = UserId(0); + int sentMessageCount = 0; + int averageCharacterCount = 0; +}; + +struct StatisticsAdministratorActionsInfo final { + UserId userId = UserId(0); + int deletedMessageCount = 0; + int bannedUserCount = 0; + int restrictedUserCount = 0; +}; + +struct StatisticsInviterInfo final { + UserId userId = UserId(0); + int addedMemberCount = 0; +}; + +struct StatisticalGraph final { +}; + +struct StatisticalValue final { + float64 value = 0.; + float64 previousValue = 0.; + float64 growthRatePercentage = 0.; +}; + +struct ChannelStatistics final { + [[nodiscard]] bool empty() const { + return !startDate || !endDate; + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + + int startDate = 0; + int endDate = 0; + + StatisticalValue memberCount; + StatisticalValue meanViewCount; + StatisticalValue meanShareCount; + + float64 enabledNotificationsPercentage = 0.; + + StatisticalGraph memberCountGraph; + StatisticalGraph joinGraph; + StatisticalGraph muteGraph; + StatisticalGraph viewCountByHourGraph; + StatisticalGraph viewCountBySourceGraph; + StatisticalGraph joinBySourceGraph; + StatisticalGraph languageGraph; + StatisticalGraph messageInteractionGraph; + StatisticalGraph instantViewInteractionGraph; + + std::vector recentMessageInteractions; + +}; + +struct SupergroupStatistics final { + [[nodiscard]] bool empty() const { + return !startDate || !endDate; + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + + int startDate = 0; + int endDate = 0; + + StatisticalValue memberCount; + StatisticalValue messageCount; + StatisticalValue viewerCount; + StatisticalValue senderCount; + + StatisticalGraph memberCountGraph; + StatisticalGraph joinGraph; + StatisticalGraph joinBySourceGraph; + StatisticalGraph languageGraph; + StatisticalGraph messageContentGraph; + StatisticalGraph actionGraph; + StatisticalGraph dayGraph; + StatisticalGraph weekGraph; + + std::vector topSenders; + std::vector topAdministrators; + std::vector topInviters; + +}; + +} // namespace Data From 177a7eaf43d387add3a374692aa01af4972fdb0a Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 26 Apr 2023 23:58:03 +0300 Subject: [PATCH 045/262] Added initial serialization from TL data to statistics data. --- Telegram/SourceFiles/api/api_statistics.cpp | 78 ++++++++++++++++++++- Telegram/SourceFiles/api/api_statistics.h | 8 ++- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 839fa98fd..62cdf05b4 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -8,20 +8,92 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_statistics.h" #include "apiwrap.h" +#include "data/data_channel.h" #include "data/data_peer.h" #include "main/main_session.h" namespace Api { namespace { + +[[nodiscard]] Data::StatisticalValue StatisticalValueFromTL( + const MTPStatsAbsValueAndPrev &tl) { + const auto current = tl.data().vcurrent().v; + const auto previous = tl.data().vprevious().v; + return Data::StatisticalValue{ + .value = current, + .previousValue = previous, + .growthRatePercentage = previous + ? std::abs((current - previous) / float64(previous) * 100.) + : 0, + }; +} + +[[nodiscard]] Data::ChannelStatistics ChannelStatisticsFromTL( + const MTPDstats_broadcastStats &data) { + const auto &tlUnmuted = data.venabled_notifications().data(); + const auto unmuted = (!tlUnmuted.vtotal().v) + ? 0. + : std::clamp( + tlUnmuted.vpart().v / tlUnmuted.vtotal().v * 100., + 0., + 100.); + using Recent = MTPMessageInteractionCounters; + auto recentMessages = ranges::views::all( + data.vrecent_message_interactions().v + ) | ranges::views::transform([&](const Recent &tl) { + return Data::StatisticsMessageInteractionInfo{ + .messageId = tl.data().vmsg_id().v, + .viewsCount = tl.data().vviews().v, + .forwardsCount = tl.data().vforwards().v, + }; + }) | ranges::to_vector; + return { + .startDate = data.vperiod().data().vmin_date().v, + .endDate = data.vperiod().data().vmax_date().v, + + .memberCount = StatisticalValueFromTL(data.vfollowers()), + .meanViewCount = StatisticalValueFromTL(data.vviews_per_post()), + .meanShareCount = StatisticalValueFromTL(data.vshares_per_post()), + + .enabledNotificationsPercentage = unmuted, + + .recentMessageInteractions = std::move(recentMessages), + }; +} + } // namespace Statistics::Statistics(not_null api) : _api(&api->instance()) { } -rpl::producer<> Statistics::request( - not_null peer) const { - return rpl::never<>(); +rpl::producer Statistics::request( + not_null peer) { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + const auto channel = peer->asChannel(); + if (!channel) { + return lifetime; + } + + if (!channel->isMegagroup()) { + _api.request(MTPstats_GetBroadcastStats( + MTP_flags(MTPstats_GetBroadcastStats::Flags(0)), + channel->inputChannel + )).done([=](const MTPstats_BroadcastStats &result) { + _channelStats = ChannelStatisticsFromTL(result.data()); + consumer.put_done(); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + } + + return lifetime; + }; +} + +Data::ChannelStatistics Statistics::channelStats() const { + return _channelStats; } } // namespace Api diff --git a/Telegram/SourceFiles/api/api_statistics.h b/Telegram/SourceFiles/api/api_statistics.h index 2197c9f82..6591976e7 100644 --- a/Telegram/SourceFiles/api/api_statistics.h +++ b/Telegram/SourceFiles/api/api_statistics.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "data/data_statistics.h" #include "mtproto/sender.h" class ApiWrap; @@ -18,10 +19,13 @@ class Statistics final { public: explicit Statistics(not_null api); - [[nodiscard]] rpl::producer<> request( - not_null peer) const; + [[nodiscard]] rpl::producer request( + not_null peer); + + [[nodiscard]] Data::ChannelStatistics channelStats() const; private: + Data::ChannelStatistics _channelStats; MTP::Sender _api; }; From b5b70beea0ebe155e2830cb9433c4caacc1652d1 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 2 May 2023 17:22:38 +0300 Subject: [PATCH 046/262] Added implementation of class for segment tree. --- Telegram/CMakeLists.txt | 2 + .../SourceFiles/statistics/segment_tree.cpp | 151 ++++++++++++++++++ .../SourceFiles/statistics/segment_tree.h | 69 ++++++++ 3 files changed, 222 insertions(+) create mode 100644 Telegram/SourceFiles/statistics/segment_tree.cpp create mode 100644 Telegram/SourceFiles/statistics/segment_tree.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index ca2fccad8..7be00c5e5 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1282,6 +1282,8 @@ PRIVATE settings/settings_type.h settings/settings_websites.cpp settings/settings_websites.h + statistics/segment_tree.cpp + statistics/segment_tree.h statistics/statistics_box.cpp statistics/statistics_box.h storage/details/storage_file_utilities.cpp diff --git a/Telegram/SourceFiles/statistics/segment_tree.cpp b/Telegram/SourceFiles/statistics/segment_tree.cpp new file mode 100644 index 000000000..385560964 --- /dev/null +++ b/Telegram/SourceFiles/statistics/segment_tree.cpp @@ -0,0 +1,151 @@ +/* +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 "statistics/segment_tree.h" + +namespace Statistic { +namespace { + +constexpr auto kMinArraySize = size_t(30); + +} // namespace + +SegmentTree::SegmentTree(std::vector array) +: _array(std::move(array)) { + if (_array.size() < kMinArraySize) { + return; + } + + // The max size of this array is about 2 * 2 ^ log2(n) + 1. + const auto size = 2 * std::pow( + 2., + std::floor((std::logf(_array.size()) / std::logf(2.)) + 1)); + _heap.resize(int(size)); + build(1, 0, _array.size()); +} + +void SegmentTree::build(int v, int from, int size) { + _heap[v].from = from; + _heap[v].to = (from + size - 1); + + if (size == 1) { + _heap[v].sum = _array[from]; + _heap[v].max = _array[from]; + _heap[v].min = _array[from]; + } else { + // Build childs. + build(2 * v, from, size / 2); + build(2 * v + 1, from + size / 2, size - size / 2); + + _heap[v].sum = _heap[2 * v].sum + _heap[2 * v + 1].sum; + // max = max of the children. + _heap[v].max = std::max(_heap[2 * v].max, _heap[2 * v + 1].max); + _heap[v].min = std::min(_heap[2 * v].min, _heap[2 * v + 1].min); + } +} + +int SegmentTree::rMaxQ(int from, int to) { + if (_array.size() < kMinArraySize) { + auto max = std::numeric_limits::min(); + from = std::max(from, 0); + to = std::min(to, int(_array.size() - 1)); + for (auto i = from; i <= to; i++) { + max = std::max(max, _array[i]); + } + return max; + } + return rMaxQ(1, from, to); +} + +int SegmentTree::rMaxQ(int v, int from, int to) { + const auto &n = _heap[v]; + // If you did a range update that contained this node, + // you can infer the Min value without going down the tree. + if (n.pendingVal && contains(n.from, n.to, from, to)) { + return n.pendingVal.value; + } + + if (contains(from, to, n.from, n.to)) { + return _heap[v].max; + } + + if (intersects(from, to, n.from, n.to)) { + propagate(v); + const auto leftMin = rMaxQ(2 * v, from, to); + const auto rightMin = rMaxQ(2 * v + 1, from, to); + + return std::max(leftMin, rightMin); + } + + return 0; +} + +int SegmentTree::rMinQ(int from, int to) { + if (_array.size() < kMinArraySize) { + auto min = std::numeric_limits::max(); + from = std::max(from, 0); + to = std::min(to, int(_array.size() - 1)); + for (auto i = from; i <= to; i++) { + min = std::min(min, _array[i]); + } + return min; + } + return rMinQ(1, from, to); +} + +int SegmentTree::rMinQ(int v, int from, int to) { + const auto &n = _heap[v]; + // If you did a range update that contained this node, + // you can infer the Min value without going down the tree. + if (n.pendingVal && contains(n.from, n.to, from, to)) { + return n.pendingVal.value; + } + + if (contains(from, to, n.from, n.to)) { + return _heap[v].min; + } + + if (intersects(from, to, n.from, n.to)) { + propagate(v); + const auto leftMin = rMinQ(2 * v, from, to); + const auto rightMin = rMinQ(2 * v + 1, from, to); + + return std::min(leftMin, rightMin); + } + + return std::numeric_limits::max(); +} + +void SegmentTree::propagate(int v) { + auto &n = _heap[v]; + + if (n.pendingVal) { + const auto value = n.pendingVal.value; + n.pendingVal = {}; + change(_heap[2 * v], value); + change(_heap[2 * v + 1], value); + } +} + +void SegmentTree::change(SegmentTree::Node &n, int value) { + n.pendingVal = { value, true }; + n.sum = n.size() * value; + n.max = value; + n.min = value; + _array[n.from] = value; +} + +bool SegmentTree::contains(int from1, int to1, int from2, int to2) const { + return (from2 >= from1) && (to2 <= to1); +} + +bool SegmentTree::intersects(int from1, int to1, int from2, int to2) const { + return ((from1 <= from2) && (to1 >= from2)) // (.[..)..] or (.[...]..) + || ((from1 >= from2) && (from1 <= to2)); // [.(..]..) or [..(..).. +} + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/segment_tree.h b/Telegram/SourceFiles/statistics/segment_tree.h new file mode 100644 index 000000000..e67adc76a --- /dev/null +++ b/Telegram/SourceFiles/statistics/segment_tree.h @@ -0,0 +1,69 @@ +/* +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 Statistic { + +class SegmentTree final { +public: + SegmentTree() = default; + SegmentTree(std::vector array); + + [[nodiscard]] bool empty() const { + return _array.empty(); + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + + [[nodiscard]] int rMaxQ(int from, int to); + [[nodiscard]] int rMinQ(int from, int to); + +private: + struct Node final { + int sum = 0; + int max = 0; + int min = 0; + + struct PendingVal { + [[nodiscard]] explicit operator bool() const { + return available; + } + int value = 0; + bool available = false; + }; + PendingVal pendingVal; + + int from = 0; + int to = 0; + + [[nodiscard]] int size() { + return to - from + 1; + } + }; + + void build(int v, int from, int size); + void propagate(int v); + void change(Node &n, int value); + + [[nodiscard]] int rMaxQ(int v, int from, int to); + [[nodiscard]] int rMinQ(int v, int from, int to); + + [[nodiscard]] bool contains(int from1, int to1, int from2, int to2) const; + [[nodiscard]] bool intersects( + int from1, + int to1, + int from2, + int to2) const; + + std::vector _array; + std::vector _heap; + +}; + +} // namespace Statistic From 78e553b7249cbf683183e29eefaee7997137407e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 2 May 2023 17:31:56 +0300 Subject: [PATCH 047/262] Added usage of segment tree in Data class for statistics. --- Telegram/SourceFiles/data/data_statistics.cpp | 141 ++++++++++++++++++ Telegram/SourceFiles/data/data_statistics.h | 49 ++++++ 2 files changed, 190 insertions(+) diff --git a/Telegram/SourceFiles/data/data_statistics.cpp b/Telegram/SourceFiles/data/data_statistics.cpp index 604797551..d66353026 100644 --- a/Telegram/SourceFiles/data/data_statistics.cpp +++ b/Telegram/SourceFiles/data/data_statistics.cpp @@ -11,4 +11,145 @@ namespace Data { namespace { } // namespace +void StatisticalChart::measure() { + if (x.empty()) { + return; + } + const auto n = x.size(); + const auto start = x.front(); + const auto end = x.back(); + + xPercentage.clear(); + xPercentage.resize(n); + if (n == 1) { + xPercentage[0] = 1; + } else { + for (auto i = 0; i < n; i++) { + xPercentage[i] = (x[i] - start) / float64(end - start); + } + } + + for (auto i = 0; i < lines.size(); i++) { + if (lines[i].maxValue > maxValue) { + maxValue = lines[i].maxValue; + } + if (lines[i].minValue < minValue) { + minValue = lines[i].minValue; + } + lines[i].segmentTree = Statistic::SegmentTree(lines[i].y); + } + + daysLookup.clear(); + const auto dateCount = int((end - start) / timeStep) + 10; + daysLookup.reserve(dateCount); + constexpr auto kOneDay = 3600 * 24 * 1000; + const auto formatter = u"MMM d"_q; + for (auto i = 0; i < dateCount; i++) { + const auto r = (start + (i * timeStep)) / 1000; + const auto dateTime = QDateTime::fromSecsSinceEpoch(r); + if (timeStep == 1) { + daysLookup.push_back( + QString(((i < 10) ? u"0%1:00"_q : u"%1:00"_q).arg(i))); + } else if (timeStep < kOneDay) { + daysLookup.push_back(u"%1:%2"_q + .arg(dateTime.time().hour(), 2, 10, QChar('0')) + .arg(dateTime.time().minute(), 2, 10, QChar('0'))); + } else { + const auto date = dateTime.date(); + daysLookup.push_back(QLocale().toString(date, formatter)); + } + } + + oneDayPercentage = timeStep / float64(end - start); +} + +QString StatisticalChart::getDayString(int i) const { + return daysLookup[int((x[i] - x[0]) / timeStep)]; +} + +int StatisticalChart::findStartIndex(float v) const { + if (v == 0) { + return 0; + } + const auto n = int(xPercentage.size()); + + if (n < 2) { + return 0; + } + auto left = 0; + auto right = n - 1; + + while (left <= right) { + const auto middle = (right + left) >> 1; + if (v < xPercentage[middle] + && (middle == 0 || v > xPercentage[middle - 1])) { + return middle; + } + if (v == xPercentage[middle]) { + return middle; + } + if (v < xPercentage[middle]) { + right = middle - 1; + } else if (v > xPercentage[middle]) { + left = middle + 1; + } + } + return left; +} + +int StatisticalChart::findEndIndex(int left, float v) const { + const auto n = int(xPercentage.size()); + if (v == 1.) { + return n - 1; + } + auto right = n - 1; + + while (left <= right) { + const auto middle = (right + left) >> 1; + if (v > xPercentage[middle] + && (middle == n - 1 || v < xPercentage[middle + 1])) { + return middle; + } + if (v == xPercentage[middle]) { + return middle; + } + if (v < xPercentage[middle]) { + right = middle - 1; + } else if (v > xPercentage[middle]) { + left = middle + 1; + } + } + return right; +} + + +int StatisticalChart::findIndex(int left, int right, float v) const { + const auto n = int(xPercentage.size()); + + if (v <= xPercentage[left]) { + return left; + } + if (v >= xPercentage[right]) { + return right; + } + + while (left <= right) { + const auto middle = (right + left) >> 1; + if (v > xPercentage[middle] + && (middle == n - 1 || v < xPercentage[middle + 1])) { + return middle; + } + + if (v == xPercentage[middle]) { + return middle; + } + if (v < xPercentage[middle]) { + right = middle - 1; + } else if (v > xPercentage[middle]) { + left = middle + 1; + } + } + return right; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_statistics.h b/Telegram/SourceFiles/data/data_statistics.h index dd0c64071..fbfe0f913 100644 --- a/Telegram/SourceFiles/data/data_statistics.h +++ b/Telegram/SourceFiles/data/data_statistics.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "statistics/segment_tree.h" + namespace Data { struct StatisticsMessageInteractionInfo final { @@ -33,7 +35,54 @@ struct StatisticsInviterInfo final { int addedMemberCount = 0; }; +struct StatisticalChart { + StatisticalChart() = default; + + [[nodiscard]] bool empty() const { + return lines.empty(); + } + [[nodiscard]] explicit operator bool() const { + return !empty(); + } + + void measure(); + + [[nodiscard]] QString getDayString(int i) const; + + [[nodiscard]] int findStartIndex(float v) const; + [[nodiscard]] int findEndIndex(int left, float v) const; + [[nodiscard]] int findIndex(int left, int right, float v) const; + + struct Line final { + std::vector y; + + Statistic::SegmentTree segmentTree; + QString id; + QString name; + int maxValue = 0; + int minValue = std::numeric_limits::max(); + int colorKey = 0; + QColor color; + QColor colorDark; + }; + + std::vector x; + std::vector xPercentage; + std::vector daysLookup; + + std::vector lines; + + int maxValue = 0; + int minValue = std::numeric_limits::max(); + + float64 oneDayPercentage = 0.; + + float64 timeStep = 0.; + +}; + struct StatisticalGraph final { + StatisticalChart chart; }; struct StatisticalValue final { From c0219cb95d51c0da21b330c4ae62c8dbb154bbaa Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 8 Sep 2023 17:18:42 +0300 Subject: [PATCH 048/262] Added deserialization from JSON to statistics data. --- Telegram/CMakeLists.txt | 2 + .../statistics_data_deserialize.cpp | 94 +++++++++++++++++++ .../statistics/statistics_data_deserialize.h | 21 +++++ 3 files changed, 117 insertions(+) create mode 100644 Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp create mode 100644 Telegram/SourceFiles/statistics/statistics_data_deserialize.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 7be00c5e5..e3b10de2b 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1286,6 +1286,8 @@ PRIVATE statistics/segment_tree.h statistics/statistics_box.cpp statistics/statistics_box.h + statistics/statistics_data_deserialize.cpp + statistics/statistics_data_deserialize.h storage/details/storage_file_utilities.cpp storage/details/storage_file_utilities.h storage/details/storage_settings_scheme.cpp diff --git a/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp b/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp new file mode 100644 index 000000000..d0d8543f7 --- /dev/null +++ b/Telegram/SourceFiles/statistics/statistics_data_deserialize.cpp @@ -0,0 +1,94 @@ +/* +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 "statistics/statistics_data_deserialize.h" + +#include "data/data_statistics.h" + +#include +#include +#include +#include + +namespace Statistic { + +Data::StatisticalChart StatisticalChartFromJSON(const QByteArray &json) { + auto error = QJsonParseError{ 0, QJsonParseError::NoError }; + const auto document = QJsonDocument::fromJson(json, &error); + if (error.error != QJsonParseError::NoError || !document.isObject()) { + LOG(("API Error: Bad stats graph json received.")); + return {}; + } + const auto root = document.object(); + const auto columns = root.value(u"columns"_q).toArray(); + if (columns.empty()) { + LOG(("API Error: Empty columns list from stats graph received.")); + return {}; + } + auto result = Data::StatisticalChart(); + for (const auto &column : columns) { + const auto array = column.toArray(); + if (array.empty()) { + LOG(("API Error: Empty column from stats graph received.")); + return {}; + } + const auto columnId = array.first().toString(); + if (columnId == u"x"_q) { + const auto length = array.size() - 1; + result.x.resize(length); + for (auto i = 0; i < length; i++) { + result.x[i] = array.at(i + 1).toDouble(); + } + } else { + auto line = Data::StatisticalChart::Line(); + const auto length = array.size() - 1; + line.id = columnId; + line.y.resize(length); + for (auto i = 0; i < length; i++) { + const auto value = array.at(i + 1).toInt(); + line.y[i] = value; + if (value > line.maxValue) { + line.maxValue = value; + } + if (value < line.minValue) { + line.minValue = value; + } + } + result.lines.push_back(std::move(line)); + } + if (result.x.size() > 1) { + result.timeStep = result.x[1] - result.x[0]; + } else { + constexpr auto kOneDay = 3600 * 24 * 1000; + result.timeStep = kOneDay; + } + result.measure(); + } + const auto colors = root.value(u"colors"_q).toObject(); + const auto names = root.value(u"names"_q).toObject(); + + const auto colorPattern = u"(.*)(#.*)"_q; + for (auto &line : result.lines) { + const auto colorIt = colors.constFind(line.id); + if (colorIt != colors.constEnd() && (*colorIt).isString()) { + const auto match = QRegularExpression(colorPattern).match( + colorIt->toString()); + if (match.hasMatch()) { + // const auto colorKey = match.captured(1); + line.color = QColor(match.captured(2)); + } + } + const auto nameIt = names.constFind(line.id); + if (nameIt != names.constEnd() && (*nameIt).isString()) { + line.name = nameIt->toString().replace('-', QChar(8212)); + } + } + + return result; +} + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/statistics_data_deserialize.h b/Telegram/SourceFiles/statistics/statistics_data_deserialize.h new file mode 100644 index 000000000..8e04b70a7 --- /dev/null +++ b/Telegram/SourceFiles/statistics/statistics_data_deserialize.h @@ -0,0 +1,21 @@ +/* +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 Data { +struct StatisticalChart; +} // namespace Data + +class QByteArray; + +namespace Statistic { + +[[nodiscard]] Data::StatisticalChart StatisticalChartFromJSON( + const QByteArray &json); + +} // namespace Statistic From 029e0c94884e097ece44329f8a5090a9e68a7d32 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 2 May 2023 17:33:17 +0300 Subject: [PATCH 049/262] Added deserialization from JSON to statistics data to API. --- Telegram/SourceFiles/api/api_statistics.cpp | 43 +++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 62cdf05b4..42c016930 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -11,10 +11,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_peer.h" #include "main/main_session.h" +#include "statistics/statistics_data_deserialize.h" namespace Api { namespace { +[[nodiscard]] Data::StatisticalGraph StatisticalGraphFromTL( + const MTPStatsGraph &tl) { + return tl.match([&](const MTPDstatsGraph &d) { + using namespace Statistic; + return Data::StatisticalGraph{ + StatisticalChartFromJSON(qs(d.vjson().data().vdata()).toUtf8()), + }; + }, [&](const MTPDstatsGraphAsync &data) { + return Data::StatisticalGraph{ Data::StatisticalChart() }; + }, [&](const MTPDstatsGraphError &data) { + return Data::StatisticalGraph{ Data::StatisticalChart() }; + }); +} + [[nodiscard]] Data::StatisticalValue StatisticalValueFromTL( const MTPStatsAbsValueAndPrev &tl) { const auto current = tl.data().vcurrent().v; @@ -47,6 +62,7 @@ namespace { .forwardsCount = tl.data().vforwards().v, }; }) | ranges::to_vector; + return { .startDate = data.vperiod().data().vmin_date().v, .endDate = data.vperiod().data().vmax_date().v, @@ -57,6 +73,33 @@ namespace { .enabledNotificationsPercentage = unmuted, + .memberCountGraph = StatisticalGraphFromTL( + data.vgrowth_graph()), + + .joinGraph = StatisticalGraphFromTL( + data.vfollowers_graph()), + + .muteGraph = StatisticalGraphFromTL( + data.vmute_graph()), + + .viewCountByHourGraph = StatisticalGraphFromTL( + data.vtop_hours_graph()), + + .viewCountBySourceGraph = StatisticalGraphFromTL( + data.vviews_by_source_graph()), + + .joinBySourceGraph = StatisticalGraphFromTL( + data.vnew_followers_by_source_graph()), + + .languageGraph = StatisticalGraphFromTL( + data.vlanguages_graph()), + + .messageInteractionGraph = StatisticalGraphFromTL( + data.vinteractions_graph()), + + .instantViewInteractionGraph = StatisticalGraphFromTL( + data.viv_interactions_graph()), + .recentMessageInteractions = std::move(recentMessages), }; } From 06948ad15e5b0121cd4acebfaec5a3e87d5c8538 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 24 May 2023 15:38:22 +0300 Subject: [PATCH 050/262] Added Data class for horizontal lines on statistic charts. --- Telegram/CMakeLists.txt | 2 + .../chart_horizontal_lines_data.cpp | 100 ++++++++++++++++++ .../statistics/chart_horizontal_lines_data.h | 33 ++++++ 3 files changed, 135 insertions(+) create mode 100644 Telegram/SourceFiles/statistics/chart_horizontal_lines_data.cpp create mode 100644 Telegram/SourceFiles/statistics/chart_horizontal_lines_data.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index e3b10de2b..2118efdca 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1282,6 +1282,8 @@ PRIVATE settings/settings_type.h settings/settings_websites.cpp settings/settings_websites.h + statistics/chart_horizontal_lines_data.cpp + statistics/chart_horizontal_lines_data.h statistics/segment_tree.cpp statistics/segment_tree.h statistics/statistics_box.cpp diff --git a/Telegram/SourceFiles/statistics/chart_horizontal_lines_data.cpp b/Telegram/SourceFiles/statistics/chart_horizontal_lines_data.cpp new file mode 100644 index 000000000..2e80f57bd --- /dev/null +++ b/Telegram/SourceFiles/statistics/chart_horizontal_lines_data.cpp @@ -0,0 +1,100 @@ +/* +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 "statistics/chart_horizontal_lines_data.h" + +#include "lang/lang_tag.h" + +namespace Statistic { +namespace { + +constexpr auto kMinLines = int(2); +constexpr auto kMaxLines = int(6); +constexpr auto kStep = 5.; + +[[nodiscard]] int Round(int maxValue) { + const auto k = int(maxValue / kStep); + return (k % 10 == 0) ? maxValue : ((maxValue / 10 + 1) * 10); +} + +} // namespace + +ChartHorizontalLinesData::ChartHorizontalLinesData( + int newMaxHeight, + int newMinHeight, + bool useMinHeight) { + if (!useMinHeight) { + const auto v = (newMaxHeight > 100) + ? Round(newMaxHeight) + : newMaxHeight; + + const auto step = std::max(1, int(std::ceil(v / kStep))); + + auto n = kMaxLines; + if (v < kMaxLines) { + n = std::max(2, v + 1); + } else if (v / 2 < kMaxLines) { + n = v / 2 + 1; + if (v % 2 != 0) { + n++; + } + } + + lines.resize(n); + + for (auto i = 1; i < n; i++) { + auto &line = lines[i]; + line.absoluteValue = i * step; + line.caption = Lang::FormatCountToShort( + line.absoluteValue).string; + } + } else { + auto n = int(0); + const auto diff = newMaxHeight - newMinHeight; + auto step = 0.; + if (diff == 0) { + newMinHeight--; + n = kMaxLines / 2; + step = 1.; + } else if (diff < kMaxLines) { + n = std::max(kMinLines, diff + 1); + step = 1.; + } else if (diff / 2 < kMaxLines) { + n = diff / 2 + diff % 2 + 1; + step = 2.; + } else { + step = (newMaxHeight - newMinHeight) / kStep; + if (step <= 0) { + step = 1; + n = std::max(kMinLines, newMaxHeight - newMinHeight + 1); + } else { + n = 6; + } + } + + lines.resize(n); + const auto diffAbsoluteValue = int((n - 1) * step); + for (auto i = 0; i < n; i++) { + auto &line = lines[i]; + const auto value = int(i * step); + line.absoluteValue = newMinHeight + value; + line.relativeValue = value / float64(diffAbsoluteValue); + line.caption = (line.absoluteValue >= 10'000) + ? Lang::FormatCountToShort(line.absoluteValue).string + : QString("%L1").arg(line.absoluteValue); + } + } +} + +int ChartHorizontalLinesData::LookupHeight(int maxValue) { + const auto v = (maxValue > 100) ? Round(maxValue) : maxValue; + + const auto step = int(std::ceil(v / kStep)); + return step * kStep; +} + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/chart_horizontal_lines_data.h b/Telegram/SourceFiles/statistics/chart_horizontal_lines_data.h new file mode 100644 index 000000000..5c3be7359 --- /dev/null +++ b/Telegram/SourceFiles/statistics/chart_horizontal_lines_data.h @@ -0,0 +1,33 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +namespace Statistic { + +struct ChartHorizontalLinesData final { +public: + ChartHorizontalLinesData( + int newMaxHeight, + int newMinHeight, + bool useMinHeight); + + [[nodiscard]] static int LookupHeight(int maxValue); + + struct Line final { + float64 absoluteValue = 0.; + float64 relativeValue = 0.; + QString caption; + }; + + std::vector lines; + float64 alpha = 0.; + float64 fixedAlpha = 1.; + +}; + +} // namespace Statistic From c9eb9a3ee0fd3477abfdcc1bc892c248d3149b98 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 24 May 2023 18:30:30 +0300 Subject: [PATCH 051/262] Added initial widget with full zoom static linear chart. --- Telegram/CMakeLists.txt | 4 + .../SourceFiles/statistics/chart_widget.cpp | 213 ++++++++++++++++++ .../SourceFiles/statistics/chart_widget.h | 55 +++++ .../statistics/linear_chart_view.cpp | 65 ++++++ .../statistics/linear_chart_view.h | 21 ++ .../SourceFiles/statistics/statistics_box.cpp | 15 ++ 6 files changed, 373 insertions(+) create mode 100644 Telegram/SourceFiles/statistics/chart_widget.cpp create mode 100644 Telegram/SourceFiles/statistics/chart_widget.h create mode 100644 Telegram/SourceFiles/statistics/linear_chart_view.cpp create mode 100644 Telegram/SourceFiles/statistics/linear_chart_view.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 2118efdca..8be69b082 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1284,6 +1284,10 @@ PRIVATE settings/settings_websites.h statistics/chart_horizontal_lines_data.cpp statistics/chart_horizontal_lines_data.h + statistics/chart_widget.cpp + statistics/chart_widget.h + statistics/linear_chart_view.cpp + statistics/linear_chart_view.h statistics/segment_tree.cpp statistics/segment_tree.h statistics/statistics_box.cpp diff --git a/Telegram/SourceFiles/statistics/chart_widget.cpp b/Telegram/SourceFiles/statistics/chart_widget.cpp new file mode 100644 index 000000000..84165775e --- /dev/null +++ b/Telegram/SourceFiles/statistics/chart_widget.cpp @@ -0,0 +1,213 @@ +/* +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 "statistics/chart_widget.h" + +#include "statistics/linear_chart_view.h" +#include "ui/rect.h" +#include "styles/style_boxes.h" + +namespace Statistic { + +namespace { + +constexpr auto kHeightLimitsUpdateTimeout = crl::time(320); + +[[nodiscard]] int FindMaxValue( + Data::StatisticalChart &chartData, + int startXIndex, + int endXIndex) { + auto maxValue = 0; + for (auto &l : chartData.lines) { + const auto lineMax = l.segmentTree.rMaxQ(startXIndex, endXIndex); + maxValue = std::max(lineMax, maxValue); + } + return maxValue; +} + +[[nodiscard]] int FindMinValue( + Data::StatisticalChart &chartData, + int startXIndex, + int endXIndex) { + auto minValue = std::numeric_limits::max(); + for (auto &l : chartData.lines) { + const auto lineMin = l.segmentTree.rMinQ(startXIndex, endXIndex); + minValue = std::min(lineMin, minValue); + } + return minValue; +} + +void PaintHorizontalLines( + QPainter &p, + const ChartHorizontalLinesData &horizontalLine, + const QRect &r) { + for (const auto &line : horizontalLine.lines) { + const auto lineRect = QRect( + 0, + r.y() + r.height() * line.relativeValue, + r.x() + r.width(), + st::lineWidth); + p.fillRect(lineRect, st::boxTextFg); + } +} + +void PaintCaptionsToHorizontalLines( + QPainter &p, + const ChartHorizontalLinesData &horizontalLine, + const QRect &r) { + p.setFont(st::boxTextFont->f); + p.setPen(st::boxTextFg); + for (const auto &line : horizontalLine.lines) { + p.drawText(10, r.y() + r.height() * line.relativeValue, line.caption); + } +} + +} // namespace + +ChartWidget::ChartWidget(not_null parent) +: Ui::RpWidget(parent) { + resize(width(), st::confirmMaxHeight); +} + +void ChartWidget::setChartData(Data::StatisticalChart chartData) { + _chartData = chartData; + + { + const auto startXIndex = _chartData.findStartIndex( + _chartData.xPercentage.front()); + const auto endXIndex = _chartData.findEndIndex( + startXIndex, + _chartData.xPercentage.back()); + setHeightLimits( + { + float64(FindMinValue(_chartData, startXIndex, endXIndex)), + float64(FindMaxValue(_chartData, startXIndex, endXIndex)), + }, + true); + update(); + } +} + +void ChartWidget::paintEvent(QPaintEvent *e) { + auto p = QPainter(this); + + const auto r = rect(); + const auto captionRect = r; + const auto chartRect = r + - QMargins{ 0, st::boxTextFont->height, 0, st::lineWidth }; + + for (const auto &horizontalLine : _horizontalLines) { + PaintHorizontalLines(p, horizontalLine, chartRect); + } + + if (_chartData) { + Statistic::PaintLinearChartView( + p, + _chartData, + chartRect); + } + + for (const auto &horizontalLine : _horizontalLines) { + PaintCaptionsToHorizontalLines(p, horizontalLine, chartRect); + } +} + +void ChartWidget::setHeightLimits(Limits newHeight, bool animated) { + { + const auto lineMaxHeight = ChartHorizontalLinesData::LookupHeight( + newHeight.max); + const auto diff = std::abs(lineMaxHeight - _animateToHeight.max); + const auto heightChanged = (!newHeight.max) + || (diff < _thresholdHeight.max); + if (!heightChanged && (newHeight.max == _animateToHeight.min)) { + return; + } + } + + const auto newLinesData = ChartHorizontalLinesData( + newHeight.max, + newHeight.min, + true); + newHeight = Limits{ + .min = newLinesData.lines.front().absoluteValue, + .max = newLinesData.lines.back().absoluteValue, + }; + + { + auto k = (_currentHeight.max - _currentHeight.min) + / float64(newHeight.max - newHeight.min); + if (k > 1.) { + k = 1. / k; + } + constexpr auto kUpdateStep1 = 0.1; + constexpr auto kUpdateStep2 = 0.03; + constexpr auto kUpdateStep3 = 0.045; + constexpr auto kUpdateStepThreshold1 = 0.7; + constexpr auto kUpdateStepThreshold2 = 0.1; + const auto s = (k > kUpdateStepThreshold1) + ? kUpdateStep1 + : (k < kUpdateStepThreshold2) + ? kUpdateStep2 + : kUpdateStep3; + + const auto refresh = (newHeight.max != _animateToHeight.max) + || (_useMinHeight && (newHeight.min != _animateToHeight.min)); + if (refresh) { + _startFromH = _currentHeight; + _startFrom = {}; + _minMaxUpdateStep = s; + } + } + + _animateToHeight = newHeight; + measureHeightThreshold(); + + { + const auto now = crl::now(); + if ((now - _lastHeightLimitsChanged) < kHeightLimitsUpdateTimeout) { + return; + } + _lastHeightLimitsChanged = now; + } + + if (!animated) { + _currentHeight = newHeight; + _horizontalLines.clear(); + _horizontalLines.push_back(newLinesData); + _horizontalLines.back().alpha = 1.; + return; + } + + for (auto &horizontalLine : _horizontalLines) { + horizontalLine.fixedAlpha = horizontalLine.alpha; + } + _horizontalLines.push_back(newLinesData); + + const auto callback = [=](float64 value) { + _horizontalLines.back().alpha = value; + + const auto startIt = begin(_horizontalLines); + const auto endIt = end(_horizontalLines); + for (auto it = startIt; it != (endIt - 1); it++) { + it->alpha = it->fixedAlpha * (1. - (endIt - 1)->alpha); + } + update(); + }; + _heightLimitsAnimation.start(callback, 0., 1., st::slideDuration); + +} + +void ChartWidget::measureHeightThreshold() { + const auto chartHeight = height(); + if (!_animateToHeight.max || !chartHeight) { + return; + } + _thresholdHeight.max = (_animateToHeight.max / float64(chartHeight)) + * st::boxTextFont->height; +} + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/chart_widget.h b/Telegram/SourceFiles/statistics/chart_widget.h new file mode 100644 index 000000000..162f8fe20 --- /dev/null +++ b/Telegram/SourceFiles/statistics/chart_widget.h @@ -0,0 +1,55 @@ +/* +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 "data/data_statistics.h" +#include "statistics/chart_horizontal_lines_data.h" +#include "ui/effects/animations.h" +#include "ui/rp_widget.h" + +namespace Statistic { + +class ChartWidget : public Ui::RpWidget { +public: + ChartWidget(not_null parent); + + struct Limits final { + float64 min = 0; + float64 max = 0; + }; + + void setChartData(Data::StatisticalChart chartData); + void setHeightLimits(Limits newHeight, bool animated); + +protected: + void paintEvent(QPaintEvent *e) override; + +private: + void measureHeightThreshold(); + + Data::StatisticalChart _chartData; + + bool _useMinHeight = false; + + Limits _currentHeight; + Limits _animateToHeight; + Limits _thresholdHeight = { -1, 0 }; + Limits _startFrom; + Limits _startFromH; + + float64 _minMaxUpdateStep = 0.; + + crl::time _lastHeightLimitsChanged = 0; + + std::vector _horizontalLines; + + Ui::Animations::Simple _heightLimitsAnimation; + +}; + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/linear_chart_view.cpp b/Telegram/SourceFiles/statistics/linear_chart_view.cpp new file mode 100644 index 000000000..2b0e832c0 --- /dev/null +++ b/Telegram/SourceFiles/statistics/linear_chart_view.cpp @@ -0,0 +1,65 @@ +/* +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 "statistics/linear_chart_view.h" + +#include "data/data_statistics.h" +#include "styles/style_boxes.h" + +namespace Statistic { + +void PaintLinearChartView( + QPainter &p, + const Data::StatisticalChart &chartData, + const QRect &rect) { + const auto offset = 0; + const auto currentMinHeight = rect.y(); // + const auto currentMaxHeight = rect.height() + rect.y(); // + + for (const auto &line : chartData.lines) { + const auto additionalP = (chartData.xPercentage.size() < 2) + ? 0. + : (chartData.xPercentage.front() * rect.width()); + const auto additionalPoints = 0; + + auto first = true; + auto chartPath = QPainterPath(); + + const auto startXIndex = chartData.findStartIndex( + chartData.xPercentage.front()); + const auto endXIndex = chartData.findEndIndex( + startXIndex, + chartData.xPercentage.back()); + + const auto localStart = std::max(0, startXIndex - additionalPoints); + const auto localEnd = std::min( + int(chartData.xPercentage.size() - 1), + endXIndex + additionalPoints); + + for (auto i = localStart; i <= localEnd; i++) { + if (line.y[i] < 0) { + continue; + } + const auto xPoint = chartData.xPercentage[i] * rect.width() + - offset; + const auto yPercentage = (line.y[i] - line.minValue) + / float64(line.maxValue - line.minValue); + const auto yPoint = rect.y() + (1. - yPercentage) * rect.height(); + if (first) { + first = false; + chartPath.moveTo(xPoint, yPoint); + } + chartPath.lineTo(xPoint, yPoint); + } + p.setPen(line.color); + p.setBrush(Qt::NoBrush); + p.drawPath(chartPath); + } + p.setPen(st::boxTextFg); +} + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/linear_chart_view.h b/Telegram/SourceFiles/statistics/linear_chart_view.h new file mode 100644 index 000000000..d23067a51 --- /dev/null +++ b/Telegram/SourceFiles/statistics/linear_chart_view.h @@ -0,0 +1,21 @@ +/* +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 Data { +struct StatisticalChart; +} // namespace Data + +namespace Statistic { + +void PaintLinearChartView( + QPainter &p, + const Data::StatisticalChart &chartData, + const QRect &rect); + +} // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/statistics_box.cpp b/Telegram/SourceFiles/statistics/statistics_box.cpp index 3c6457813..3476220d7 100644 --- a/Telegram/SourceFiles/statistics/statistics_box.cpp +++ b/Telegram/SourceFiles/statistics/statistics_box.cpp @@ -7,13 +7,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "statistics/statistics_box.h" +#include "api/api_statistics.h" #include "data/data_peer.h" #include "lang/lang_keys.h" +#include "main/main_session.h" +#include "statistics/chart_widget.h" #include "ui/layers/generic_box.h" namespace { } // namespace void StatisticsBox(not_null box, not_null peer) { + const auto chartWidget = box->addRow( + object_ptr(box)); + const auto api = chartWidget->lifetime().make_state( + &peer->session().api()); + + api->request( + peer + ) | rpl::start_with_done([=] { + if (const auto stats = api->channelStats()) { + chartWidget->setChartData(stats.memberCountGraph.chart); + } + }, chartWidget->lifetime()); box->setTitle(tr::lng_stats_title()); } From c71f35778d5392960788f39f1de90db0ef1b1aca Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 25 May 2023 21:53:34 +0300 Subject: [PATCH 052/262] Added API support of channel flag for channel statistics. --- Telegram/SourceFiles/data/data_channel.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index cf266c5a2..3b26c1eb9 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -997,7 +997,8 @@ void ApplyChannelUpdate( | Flag::PreHistoryHidden | Flag::AntiSpam | Flag::Location - | Flag::ParticipantsHidden; + | Flag::ParticipantsHidden + | Flag::CanGetStatistics; channel->setFlags((channel->flags() & ~mask) | (update.is_can_set_username() ? Flag::CanSetUsername : Flag()) | (update.is_can_view_participants() @@ -1009,7 +1010,8 @@ void ApplyChannelUpdate( | (update.vlocation() ? Flag::Location : Flag()) | (update.is_participants_hidden() ? Flag::ParticipantsHidden - : Flag())); + : Flag()) + | (update.is_can_view_stats() ? Flag::CanGetStatistics : Flag())); channel->setUserpicPhoto(update.vchat_photo()); if (const auto migratedFrom = update.vmigrated_from_chat_id()) { channel->addFlags(Flag::Megagroup); From 26b17325aada9de33f7a619fe983e4aceaa0dbf9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Fri, 26 May 2023 14:58:43 +0300 Subject: [PATCH 053/262] Added initial implementation of footer in statistic chart widget. --- .../SourceFiles/statistics/chart_widget.cpp | 111 +++++++++++++++++- .../SourceFiles/statistics/chart_widget.h | 2 + 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/statistics/chart_widget.cpp b/Telegram/SourceFiles/statistics/chart_widget.cpp index 84165775e..cb78a7b96 100644 --- a/Telegram/SourceFiles/statistics/chart_widget.cpp +++ b/Telegram/SourceFiles/statistics/chart_widget.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "statistics/chart_widget.h" #include "statistics/linear_chart_view.h" +#include "ui/abstract_button.h" #include "ui/rect.h" #include "styles/style_boxes.h" @@ -68,9 +69,110 @@ void PaintCaptionsToHorizontalLines( } // namespace +class ChartWidget::Footer final : public Ui::AbstractButton { +public: + Footer(not_null parent); + +private: + not_null _left; + not_null _right; + + struct { + int x = 0; + int leftLimit = 0; + int rightLimit = 0; + } _start; + +}; + +ChartWidget::Footer::Footer(not_null parent) +: Ui::AbstractButton(parent) +, _left(Ui::CreateChild(this)) +, _right(Ui::CreateChild(this)) { + sizeValue( + ) | rpl::start_with_next([=](const QSize &s) { + _left->resize(st::colorSliderWidth, s.height()); + _right->resize(st::colorSliderWidth, s.height()); + }, _left->lifetime()); + _left->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(_left); + p.setOpacity(0.3); + p.fillRect(_left->rect(), st::boxTextFg); + }, _left->lifetime()); + _right->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(_right); + p.setOpacity(0.3); + p.fillRect(_right->rect(), st::boxTextFg); + }, _right->lifetime()); + + _left->move(10, 0); + _right->move(50, 0); + + const auto handleDrag = [&]( + not_null side, + Fn leftLimit, + Fn rightLimit) { + side->events( + ) | rpl::filter([=](not_null e) { + return (e->type() == QEvent::MouseButtonPress) + || (e->type() == QEvent::MouseButtonRelease) + || ((e->type() == QEvent::MouseMove) && side->isDown()); + }) | rpl::start_with_next([=](not_null e) { + const auto pos = static_cast(e.get())->pos(); + switch (e->type()) { + case QEvent::MouseMove: { + const auto nextX = std::clamp( + side->x() + (pos.x() - _start.x), + _start.leftLimit, + _start.rightLimit); + side->move(nextX, side->y()); + } break; + case QEvent::MouseButtonPress: { + _start.x = pos.x(); + _start.leftLimit = leftLimit(); + _start.rightLimit = rightLimit(); + } break; + case QEvent::MouseButtonRelease: { + _start = {}; + } break; + } + }, side->lifetime()); + }; + handleDrag( + _left, + [=] { return 0; }, + [=] { return _right->x() - _left->width(); }); + handleDrag( + _right, + [=] { return rect::right(_left); }, + [=] { return width() - _right->width(); }); +} + ChartWidget::ChartWidget(not_null parent) -: Ui::RpWidget(parent) { - resize(width(), st::confirmMaxHeight); +: Ui::RpWidget(parent) +, _footer(std::make_unique