From 3fc4030ca6d6250962db9b94343edbb29fc47ab3 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 2 Apr 2024 10:25:46 +0400 Subject: [PATCH 01/71] Migrate xz in Dockerfile to the URL used in prepare.py --- 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 cb0fcbaa6..290146ce3 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -79,7 +79,7 @@ RUN git init zlib \ && rm -rf zlib FROM builder AS xz -RUN git clone -b v5.4.4 --depth=1 {{ GIT }}/tukaani-project/xz.git \ +RUN git clone -b v5.4.4 --depth=1 https://git.tukaani.org/xz.git \ && cd xz \ && cmake -B build . -DCMAKE_BUILD_TYPE=None \ && cmake --build build -j$(nproc) \ From e02b60d8fa05ebe4537e9fc757911cbdffe0c1ad Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 11:26:18 +0400 Subject: [PATCH 02/71] Fix placeholder in chat intro setup. --- Telegram/SourceFiles/settings/business/settings_chat_intro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp b/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp index 16072b1f0..7bd61ca90 100644 --- a/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp +++ b/Telegram/SourceFiles/settings/business/settings_chat_intro.cpp @@ -160,7 +160,7 @@ private: object_ptr( container, st::settingsChatIntroField, - tr::lng_chat_intro_enter_title(), + std::move(placeholder), current), st::settingsChatIntroFieldMargins); field->setMaxLength(limit); From 615ad85d26f5b953322e7733d7c44121163819a8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 11:26:22 +0400 Subject: [PATCH 03/71] Fix possible crash in notifications setup. Initial _account assignment triggered sync createManager(), which in case of native Windows notifications called ToastNotificationManager::CreateToastNotifier, which could enter qt-wndproc-message processing with QEvent::ApplicationActivated delivery and calling updateNonIdle() which called Window::Controller::account(), that wasn't yet set. --- Telegram/SourceFiles/main/main_domain.cpp | 6 ++++-- Telegram/SourceFiles/main/main_domain.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/main/main_domain.cpp b/Telegram/SourceFiles/main/main_domain.cpp index 26e01229f..cc6bd9457 100644 --- a/Telegram/SourceFiles/main/main_domain.cpp +++ b/Telegram/SourceFiles/main/main_domain.cpp @@ -32,11 +32,13 @@ Domain::Domain(const QString &dataName) : _dataName(dataName) , _local(std::make_unique(this, dataName)) { _active.changes( - ) | rpl::take(1) | rpl::start_with_next([] { + ) | rpl::take(1) | rpl::start_with_next([=] { // In case we had a legacy passcoded app we start settings here. Core::App().startSettingsAndBackground(); - Core::App().notifications().createManager(); + crl::on_main(this, [=] { + Core::App().notifications().createManager(); + }); }, _lifetime); _active.changes( diff --git a/Telegram/SourceFiles/main/main_domain.h b/Telegram/SourceFiles/main/main_domain.h index 1a39e8e7f..2e69222b9 100644 --- a/Telegram/SourceFiles/main/main_domain.h +++ b/Telegram/SourceFiles/main/main_domain.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/timer.h" +#include "base/weak_ptr.h" namespace Storage { class Domain; @@ -23,7 +24,7 @@ namespace Main { class Account; class Session; -class Domain final { +class Domain final : public base::has_weak_ptr { public: struct AccountWithIndex { int index = 0; From 1da9df690fe068ea62e0a58d0d963eab8c4465ac Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 11:50:58 +0400 Subject: [PATCH 04/71] Fix memory leak in downloads thumbnail loading. --- Telegram/SourceFiles/data/data_download_manager.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/data/data_download_manager.cpp b/Telegram/SourceFiles/data/data_download_manager.cpp index 95c78092d..2fa5f05d4 100644 --- a/Telegram/SourceFiles/data/data_download_manager.cpp +++ b/Telegram/SourceFiles/data/data_download_manager.cpp @@ -1123,13 +1123,14 @@ rpl::producer MakeDownloadBarContent() { state->thumbnail = Images::Prepare(embed->original(), 0, { .options = Images::Option::Blur, }); + } else if (!state->downloadTaskLifetime) { + state->document->session().downloaderTaskFinished( + ) | rpl::filter([=] { + return self(self); + }) | rpl::start_with_next( + state->push, + state->downloadTaskLifetime); } - state->document->session().downloaderTaskFinished( - ) | rpl::filter([=] { - return self(self); - }) | rpl::start_with_next( - state->push, - state->downloadTaskLifetime); return !state->thumbnail.isNull(); }; const auto resolveThumbnail = [=] { From 8ab118a8e9916a7d95d42b5bd150ebd13280d6a2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 12:20:27 +0400 Subject: [PATCH 05/71] Fix rare crashes in message sending animations. --- .../SourceFiles/history/history_widget.cpp | 13 ++++--- .../history/view/history_view_list_widget.cpp | 8 ++-- .../message_sending_animation_controller.cpp | 39 +++++++++++++------ .../message_sending_animation_controller.h | 6 ++- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 79994f61f..8d2dff310 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -6244,8 +6244,7 @@ void HistoryWidget::startItemRevealAnimations() { void HistoryWidget::startMessageSendingAnimation( not_null item) { auto &sendingAnimation = controller()->sendingAnimation(); - if (!sendingAnimation.hasLocalMessage(item->fullId().msg) - || !sendingAnimation.checkExpectedType(item)) { + if (!sendingAnimation.checkExpectedType(item)) { return; } Assert(item->mainView() != nullptr); @@ -6257,14 +6256,16 @@ void HistoryWidget::startMessageSendingAnimation( geometryValue() | rpl::to_empty, _scroll->geometryValue() | rpl::to_empty, _list->geometryValue() | rpl::to_empty - ) | rpl::map([=] { + ) | rpl::map([=]() -> std::optional { const auto view = item->mainView(); + const auto top = view ? _list->itemTop(view) : -1; + if (top < 0) { + return std::nullopt; + } const auto additional = (_list->height() == _scroll->height()) ? view->height() : 0; - return _list->mapToGlobal(QPoint( - 0, - _list->itemTop(view) - additional)); + return _list->mapToGlobal(QPoint(0, top - additional)); }); sendingAnimation.startAnimation({ diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index fb8d99130..23b7c0be3 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1839,16 +1839,18 @@ void ListWidget::startItemRevealAnimations() { void ListWidget::startMessageSendingAnimation( not_null item) { auto &sendingAnimation = controller()->sendingAnimation(); - if (!sendingAnimation.hasLocalMessage(item->fullId().msg) - || !sendingAnimation.checkExpectedType(item)) { + if (!sendingAnimation.checkExpectedType(item)) { return; } auto globalEndTopLeft = rpl::merge( session().data().newItemAdded() | rpl::to_empty, geometryValue() | rpl::to_empty - ) | rpl::map([=] { + ) | rpl::map([=]() -> std::optional { const auto view = viewForItem(item); + if (!view) { + return std::nullopt; + } const auto additional = !_visibleTop ? view->height() : 0; return mapToGlobal(QPoint(0, itemTop(view) - additional)); }); diff --git a/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp b/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp index 84d98f521..552f46412 100644 --- a/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp +++ b/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp @@ -39,7 +39,7 @@ public: Content( not_null parent, not_null controller, - const MessageSendingAnimationFrom &fromInfo, + MessageSendingAnimationFrom &&fromInfo, MessageSendingAnimationController::SendingInfoTo &&to); [[nodiscard]] rpl::producer<> destroyRequests() const; @@ -79,7 +79,7 @@ private: Content::Content( not_null parent, not_null controller, - const MessageSendingAnimationFrom &fromInfo, + MessageSendingAnimationFrom &&fromInfo, MessageSendingAnimationController::SendingInfoTo &&to) : RpWidget(parent) , _controller(controller) @@ -98,8 +98,12 @@ Content::Content( base::take( _toInfo.globalEndTopLeft ) | rpl::distinct_until_changed( - ) | rpl::start_with_next([=](const QPoint &p) { - _to = parent->mapFromGlobal(p); + ) | rpl::start_with_next([=](const std::optional &p) { + if (p) { + _to = parent->mapFromGlobal(*p); + } else { + _destroyRequests.fire({}); + } }, lifetime()); _controller->session().downloaderTaskFinished( @@ -366,6 +370,20 @@ void Content::createBubble() { MessageSendingAnimationController::MessageSendingAnimationController( not_null controller) : _controller(controller) { + subscribeToDestructions(); +} + +void MessageSendingAnimationController::subscribeToDestructions() { + _controller->session().data().itemIdChanged( + ) | rpl::start_with_next([=](Data::Session::IdChange change) { + _itemSendPending.remove(change.oldId); + }, _lifetime); + + _controller->session().data().itemRemoved( + ) | rpl::start_with_next([=](not_null item) { + _itemSendPending.remove(item->id); + _processing.remove(item); + }, _lifetime); } void MessageSendingAnimationController::appendSending( @@ -389,26 +407,23 @@ void MessageSendingAnimationController::startAnimation(SendingInfoTo &&to) { if (it == end(_itemSendPending)) { return; } - const auto msg = it->first; + auto from = std::move(it->second); + _itemSendPending.erase(it); auto content = base::make_unique_q( container, _controller, - it->second, + std::move(from), std::move(to)); + content->destroyRequests( ) | rpl::start_with_next([=] { - _itemSendPending.erase(msg); _processing.erase(item); }, content->lifetime()); _processing.emplace(item, std::move(content)); } -bool MessageSendingAnimationController::hasLocalMessage(MsgId msgId) const { - return _itemSendPending.contains(msgId); -} - bool MessageSendingAnimationController::hasAnimatedMessage( not_null item) const { return _processing.contains(item); @@ -421,7 +436,7 @@ void MessageSendingAnimationController::clear() { bool MessageSendingAnimationController::checkExpectedType( not_null item) { - const auto it = _itemSendPending.find(item->fullId().msg); + const auto it = _itemSendPending.find(item->id); if (it == end(_itemSendPending)) { return false; } diff --git a/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.h b/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.h index 92bb9a915..8250e778a 100644 --- a/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.h +++ b/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.h @@ -29,7 +29,7 @@ public: not_null controller); struct SendingInfoTo { - rpl::producer globalEndTopLeft; + rpl::producer> globalEndTopLeft; Fn view; Fn paintContext; }; @@ -37,13 +37,13 @@ public: void appendSending(MessageSendingAnimationFrom from); void startAnimation(SendingInfoTo &&to); - [[nodiscard]] bool hasLocalMessage(MsgId msgId) const; [[nodiscard]] bool hasAnimatedMessage(not_null item) const; [[nodiscard]] bool checkExpectedType(not_null item); void clear(); private: + void subscribeToDestructions(); const not_null _controller; base::flat_map _itemSendPending; @@ -52,6 +52,8 @@ private: not_null, base::unique_qptr> _processing; + rpl::lifetime _lifetime; + }; } // namespace Ui From 971803803c16b1c8761db25e7157ff889572a4ff Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 12:47:24 +0400 Subject: [PATCH 06/71] Fix ripple animation on Added sticker set button. --- Telegram/SourceFiles/boxes/stickers_box.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 01f6a1ed1..5a15a1ec9 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -1760,8 +1760,11 @@ void StickersBox::Inner::setActionDown(int newActionDown) { const auto &st = installedSet ? st::stickersTrendingInstalled : st::stickersTrendingAdd; + const auto buttonTextWidth = installedSet + ? _installedWidth + : _addWidth; auto rippleMask = Ui::RippleAnimation::RoundRectMask( - QSize(_addWidth - st.width, st.height), + QSize(buttonTextWidth - st.width, st.height), st::roundRadiusLarge); ensureRipple( st.ripple, From 9957678fb7df2dd74bf7e5d5c09f60a682713793 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 13:15:03 +0400 Subject: [PATCH 07/71] Fix adding from "Emoji from this message". --- Telegram/SourceFiles/boxes/stickers_box.cpp | 36 +++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 5a15a1ec9..0597a8fa2 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -652,8 +652,8 @@ void StickersBox::prepare() { } if (const auto featured = _featured.widget()) { featured->setInstallSetCallback([=](uint64 setId) { - markAsInstalledCallback(setId); installCallback(setId); + markAsInstalledCallback(setId); }); featured->setRemoveSetCallback(markAsRemovedCallback); } @@ -1905,9 +1905,19 @@ void StickersBox::Inner::updateSelected() { selected = selectedIndex; local.setY(local.y() - _itemsTop - selectedIndex * _rowHeight); const auto row = _rows[selectedIndex].get(); - if (!_megagroupSet && (_isInstalledTab || (_section == Section::Featured) || !row->isInstalled() || row->isArchived() || row->removed)) { + if (!_megagroupSet + && (_isInstalledTab + || (_section == Section::Featured) + || !row->isInstalled() + || row->isArchived() + || row->removed)) { auto removeButton = (_isInstalledTab && !row->removed); - auto rect = myrtlrect(relativeButtonRect(removeButton, false)); + + const auto installedSetButton = !_isInstalledTab + && row->isInstalled() + && !row->isArchived() + && !row->removed; + auto rect = myrtlrect(relativeButtonRect(removeButton, installedSetButton)); actionSel = rect.contains(local) ? selectedIndex : -1; } else { actionSel = -1; @@ -1960,12 +1970,19 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { _mouse = e->globalPos(); updateSelected(); - if (_actionDown == _actionSel && _actionSel >= 0) { - const auto callback = _rows[_actionDown]->removed - ? _installSetCallback - : _removeSetCallback; + const auto down = _actionDown; + setActionDown(-1); + if (down == _actionSel && _actionSel >= 0) { + const auto row = _rows[down].get(); + const auto installedSet = row->isInstalled() + && !row->isArchived() + && !row->removed; + const auto callback = installedSet + ? _removeSetCallback + : _installSetCallback; if (callback) { - callback(_rows[_actionDown]->set->id); + row->ripple.reset(); + callback(row->set->id); } } else if (_dragging >= 0) { _rows[_dragging]->yadd.start(0.); @@ -1976,7 +1993,7 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { } _dragging = _started = -1; - } else if (pressed == _selected && _actionSel < 0 && _actionDown < 0) { + } else if (pressed == _selected && _actionSel < 0 && down < 0) { const auto selectedIndex = [&] { if (auto index = std::get_if(&_selected)) { return *index; @@ -2000,7 +2017,6 @@ void StickersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { showSetByRow(*_megagroupSelectedSet); } } - setActionDown(-1); } void StickersBox::Inner::saveGroupSet(Fn done) { From 7fa3c6f8e82ef9f9010f1154426bc29feea619a9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 13:19:22 +0400 Subject: [PATCH 08/71] Return media viewer hiding glitch workaround. --- Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 4d3cd7f95..229b37636 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -3253,7 +3253,7 @@ not_null OverlayWidget::widget() const { void OverlayWidget::hide() { clearBeforeHide(); - //applyHideWindowWorkaround(); + applyHideWindowWorkaround(); _window->hide(); } From 587b07a648f42555ccefc15fcd2fcad034b36c4d Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 2 Apr 2024 16:26:00 +0400 Subject: [PATCH 09/71] Update Qt to 6.7.0 on Linux --- Telegram/build/docker/centos_env/Dockerfile | 2 +- snap/snapcraft.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 290146ce3..0cf935664 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.7.0" -%} -{%- set QT_TAG = "v" ~ QT ~ "-rc2" -%} +{%- set QT_TAG = "v" ~ QT -%} {%- set CMAKE_VER = "3.27.6" -%} {%- set CMAKE_FILE = "cmake-" ~ CMAKE_VER ~ "-Linux-x86_64.sh" -%} {%- set CFLAGS_DEBUG = "-g -pipe -fPIC -fstack-protector-all -fstack-clash-protection -fcf-protection -D_GLIBCXX_ASSERTIONS" -%} diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index b464b4e91..70eee8050 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -440,7 +440,7 @@ parts: override-pull: | QT=6.7.0 - git clone -b v${QT}-rc2 --depth=1 https://github.com/qt/qt5.git . + git clone -b v${QT} --depth=1 https://github.com/qt/qt5.git . git submodule update --init --recursive --depth=1 qtbase qtdeclarative qtwayland qtimageformats qtsvg qtshadertools cd qtbase From c678973119521695207c6da4559b447f54ab9242 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Tue, 2 Apr 2024 16:26:58 +0400 Subject: [PATCH 10/71] Update patches on Linux --- Telegram/build/docker/centos_env/Dockerfile | 2 +- snap/snapcraft.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 0cf935664..f5f7ab065 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -51,7 +51,7 @@ FROM builder AS patches RUN git init patches \ && cd patches \ && git remote add origin {{ GIT }}/desktop-app/patches.git \ - && git fetch --depth=1 origin 7b8f30b787691a325d17e20ddd455e87738e7432 \ + && git fetch --depth=1 origin 174d6e51435e6eab0596904f9fba80a927ef4177 \ && git reset --hard FETCH_HEAD \ && rm -rf .git diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 70eee8050..1993f7997 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -168,7 +168,7 @@ parts: patches: source: https://github.com/desktop-app/patches.git source-depth: 1 - source-commit: 7b8f30b787691a325d17e20ddd455e87738e7432 + source-commit: 174d6e51435e6eab0596904f9fba80a927ef4177 plugin: dump override-pull: | craftctl default From ef04ed447f16fb265db3b2cee95cb97fff3d8bec Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 18:18:39 +0400 Subject: [PATCH 11/71] Fix 'listening' state show in group video chats. Regression was introduced in c3ca8c6258. --- .../SourceFiles/calls/group/calls_group_members_row.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp b/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp index a9f41b35a..02ae9cd68 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp @@ -612,9 +612,9 @@ void MembersRow::paintComplexStatusText( x += skip; availableWidth -= skip; const auto &font = st::normalFont; - const auto useAbout = (style == MembersRowStyle::Video) - ? false - : ((_state == State::RaisedHand && !_raisedHandStatus) + const auto useAbout = !_about.isEmpty() + && (style != MembersRowStyle::Video) + && ((_state == State::RaisedHand && !_raisedHandStatus) || (_state != State::RaisedHand && !_speaking)); if (!useAbout && _state != State::Invited From a74c5ee7192f4898dd013dc35041c6b6eda69bc2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 22:50:58 +0400 Subject: [PATCH 12/71] Fix complex-shaped Ui::Text::String geometry. --- Telegram/lib_ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_ui b/Telegram/lib_ui index bdf4ff34f..2385fb30b 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit bdf4ff34f24ef75d5209addf756633f9db6cb232 +Subproject commit 2385fb30b5e13d3806e3aa6db489ef801e77b89b From 22f504ca21137fc5106e7123a37d2af89ecd5c0a Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 22:51:38 +0400 Subject: [PATCH 13/71] Add hidden buttons to langpack. --- Telegram/Resources/langs/lang.strings | 2 ++ Telegram/SourceFiles/window/window_main_menu.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1fedad680..d9e5dba0a 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_menu_set_status" = "Set Emoji Status"; "lng_menu_change_status" = "Change Emoji Status"; "lng_menu_my_stories" = "My Stories"; +"lng_menu_my_groups" = "My Groups"; +"lng_menu_my_channels" = "My Channels"; "lng_disable_notifications_from_tray" = "Disable notifications"; "lng_enable_notifications_from_tray" = "Enable notifications"; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index d94e1648b..0276c3771 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -218,7 +218,8 @@ not_null AddMyChannelsBox( button, st::defaultPopupMenu); (*menu)->addAction( - chats ? u"My Groups"_q : u"My Channels"_q, + (chats ? tr::lng_menu_my_groups : tr::lng_menu_my_channels)( + tr::now), [=] { controller->uiShow()->showBox(Box(myChannelsBox)); }, nullptr); (*menu)->popup(QCursor::pos()); From 1061fb6c85c3056984781b889c458ff8322e48f8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 15:35:53 +0400 Subject: [PATCH 14/71] Implement collectible username / phone info boxes. --- .../animations/collectible_phone.tgs | Bin 0 -> 3228 bytes .../animations/collectible_username.tgs | Bin 0 -> 4492 bytes Telegram/Resources/langs/lang.strings | 8 + .../Resources/qrc/telegram/animations.qrc | 2 + Telegram/SourceFiles/apiwrap.cpp | 2 +- Telegram/SourceFiles/boxes/boxes.style | 30 ++ Telegram/SourceFiles/boxes/peer_list_box.cpp | 4 +- .../boxes/peers/edit_linked_chat_box.cpp | 2 +- .../boxes/peers/prepare_short_info_box.cpp | 2 +- .../SourceFiles/boxes/premium_limits_box.cpp | 6 +- .../SourceFiles/core/local_url_handlers.cpp | 36 +++ Telegram/SourceFiles/data/data_channel.cpp | 4 + Telegram/SourceFiles/data/data_channel.h | 1 + Telegram/SourceFiles/data/data_peer.cpp | 30 +- Telegram/SourceFiles/data/data_peer.h | 6 +- Telegram/SourceFiles/data/data_session.cpp | 2 +- .../data/data_sponsored_messages.cpp | 2 +- Telegram/SourceFiles/data/data_story.cpp | 2 +- Telegram/SourceFiles/data/data_user.cpp | 4 + Telegram/SourceFiles/data/data_user.h | 16 +- Telegram/SourceFiles/data/data_user_names.cpp | 6 + Telegram/SourceFiles/data/data_user_names.h | 1 + .../dialogs/dialogs_inner_widget.cpp | 4 +- .../admin_log/history_admin_log_item.cpp | 2 +- .../earn/info_earn_inner_widget.cpp | 24 +- .../earn/info_earn_inner_widget.h | 4 + .../info/profile/info_profile_actions.cpp | 28 +- .../info/profile/info_profile_phone_menu.cpp | 27 +- .../info/profile/info_profile_phone_menu.h | 2 + .../info/profile/info_profile_values.cpp | 33 ++- .../info/profile/info_profile_values.h | 3 + Telegram/SourceFiles/main/main_domain.cpp | 2 +- .../SourceFiles/settings/settings_main.cpp | 2 +- .../ui/boxes/collectible_info_box.cpp | 271 ++++++++++++++++++ .../ui/boxes/collectible_info_box.h | 45 +++ .../SourceFiles/ui/text/format_values.cpp | 1 + .../SourceFiles/window/window_main_menu.cpp | 4 +- .../window/window_session_controller.cpp | 77 +++++ .../window/window_session_controller.h | 8 + Telegram/cmake/td_ui.cmake | 2 + 40 files changed, 630 insertions(+), 75 deletions(-) create mode 100644 Telegram/Resources/animations/collectible_phone.tgs create mode 100644 Telegram/Resources/animations/collectible_username.tgs create mode 100644 Telegram/SourceFiles/ui/boxes/collectible_info_box.cpp create mode 100644 Telegram/SourceFiles/ui/boxes/collectible_info_box.h diff --git a/Telegram/Resources/animations/collectible_phone.tgs b/Telegram/Resources/animations/collectible_phone.tgs new file mode 100644 index 0000000000000000000000000000000000000000..4e7284d5f5ab8c3630c4d68d78b3497e662d2946 GIT binary patch literal 3228 zcmV;N3}f>jiwFP!000021MOSeZXCxE{gq&!X9j)0@B^F|KLml_2tF{8WD%w$f~1`o z0{=bdRCUkJ?#_~ww6u(v5V+*_^mKLC{ZzF+Z|;A-+nm{9^V{ZZ6Q?-2&9HfQyE#i{ z*j(Lg&hYCKe#JU${)1oE4V(Av_s0)-$724QH*aL(<=x%edn|W;F>J0ce}8++zdz59 zuRgvJQun`a&cfpLyIUFl1+zbIZuoJj1M>D22mXr%CO>TOrhUMU<}njx&|vUhULr{s z)8JE?LJot$UhXmf+zjo@m3;A2#Mg&mNub0P3T20YUr?xSpTelC&P41~Z9X8gc8;B6 zxU!%U@&T2;>`Z5LlTC+&!U`c*my~>6P%37syOjE}Ga)yb;t%L#`y`>TB$Uj-k@~{h zBiYIOP6`XevL@RHJf^mY${1Cdy&3Y_x=Ni>7VYOkc6)9I{JU5jcxd@?tDAb!V=3Aqko#17qcDX6w3C|oeJFS=-}l-<&qYLm!JYBOqB+7K05f>8ZnZmnu5%6 z2?k$4*iOMm9V%0T)r}F{iaN{VoCsxO1W#h=L&*jiD#6Q8fbZd2IZV;&7QCHOz)*2h zOgd?DC1INtY*l(-aMTeWT-$i^79-eOu{`0wH)r>^zrKCg4?NyC1;AfBs*&2bu|XB1 z>?%ehIfYnGtQLOrabhYEDH{FY$7*hQT6BYMaj zA_Tj=BSnkFd=>QwVj@_5)(+QHfjfu3y8x9?iBg9whrj`!btq00J2b+()JIB4G8Bx; z)R(EiKaByoQff=p2|9yQVM{y&LUwdnRaiC<5RJHJ3}j1M5%Df^1|{phN;6iRhE1x0xwZb+`v>VUc>9f z=@9sxDWnV~t157d#McKAWv*!+$~siK2f^u(s*lyro&an4iXm&Px`Hu7nv}SYQ&MNd z5hnvdRRFoFcr3sM6pbx8%nC3QqCJwsOtJTjd?ENa$%Vm<+1GS#Yrh4Rsj zL~JqHwsI(0SN3Tp@12l`i#nC4(<83EngEFuQ%)^vkI7(%bEg(H^D-~tuk@G+$4fzb zH6bKr#yc_U61ohH7Jg0JEDHoa) z4cddnE%BOcQG*Ue4VpC^P2lp@o3^}Kc~6&JMed<#0z?+ocCBXhmHC8@i>9f8J+jRW{;xGh;iz$kdD3CJ*RW1ua}6(zmq+!nXhmm_?ewhZvJ zL~ZDq$$eo)Z7v)-@#q@yM-kjwwRcVG{xtb{P^bdD8opX!LRj-}9 zA6GmUx9PcQ={f8%=PoZv&r@yaZ{!l1o_knrnuoFuS>Ny<{5bI#%)R3=&f;2()mkZV zl0|4X7~4o`8H1+lkwCLHK_6!4i_-L85q86CcidgnZz5J|63!?sOhzPEIhV=U)GH<( zcl#1%tlV*TFSYnB?l!p(b=;k+=H?;HxI3hV&chaW8x?)t0`HazrWtsIeT|`efL(hfc4*Z*Bg0d*^H3d^`d$`KY^-aOgmry`XHmS z{ts8z*GJaBXW{u~cl}oKzFZx>+uO_Bogm%C6LVyDcfTA=ySqFXl5PKVd-?J1-KX0R zn}>@w^!M9Ozuw4E+WDS1(OfWJ^i(p_tUS2fDuV1i+Gch=A@Xi@(h6G`=HUYd8)V{I zYv`b#vrco|d-^y}*H_ zm&YY4U0Vs;I`e)(@t~e~P)i` zoGdQ_pM#4pS2llDzHcZrorHUoIGakw#xLg{ky<5EV@XYsVwCj+U?@YKl=uYi3I4Y} zX*Do!r_|4~!Z@(Joww+U`1vsg60pPr2FB_R8CZ+m zXCRZRkO_El=O8jDVC)48on5joU>$=Uh1A*S6;>gi^DVuC-VvL}cuMy9uhBnN79tyq zFQI*G5t;}+Nd*X9%Mz_cnHo1^(>~~_u&*=(?RqN#I_JbfQtuow8Dv zUcO%fEI6AiK^J~qID+$e8>GwO6(a*{dsQyezGu%4Iqc2Y#zbC&VH?|}+#yn^Rf0ne z?iK9(!C{Q7d>$c33?W*0b zOpw=h*ICk<@25s= z1RiQ!0}ms$EDuQDzkzjdd4UW7AKaI*E-86dS-Er#jdhj7D7{&6QwT7+d7e~G#JwMb0IijS4r-g ziEfs}dwFDTPa356!Pp=9yS@+x!&wV&Qv+lzbzd4bT_H^^2HsbzTx^Nrx>gizjkHggU-c zvGE~LFHKoy$3|pG1=Zp=StqwyvYTSpR5rk%j3QBK&<}FYt=&F?tV`07;98lT*ZeV- z^W#;(GsdeAb&Z9=uCTDyW!Hxet*ZZ1eCYFJ*qcYFAXxb%!OjpB&c?qXJ2dX7a8lc!|aT<1?ctOQ2n&T1Q#MZJqhWi;q?M|VE zxrc-spO;-ay{7Eyo_)sJW3DzBc>zbGvaZQCRFMoPwz3ma8ahb6;{-(V25KfN$tl<4 zbcV=cyNaSjU-8->kJnsG5``>a8O>znbQ+~H6NLKx&-v4*o3%4u?EL3GTbPR}l#;2? OsQ&@y>1%}=H~;|mPA<6s literal 0 HcmV?d00001 diff --git a/Telegram/Resources/animations/collectible_username.tgs b/Telegram/Resources/animations/collectible_username.tgs new file mode 100644 index 0000000000000000000000000000000000000000..f93fd6d746df32fb00594bfe9e5eef882c6b92b2 GIT binary patch literal 4492 zcmV;75p(VziwFo??&W0w17UP7YIARH0PS5{Z(O+z{wu*e%Llq&{MPF}6)3PpUjje4 ziFcDGj*YQ*+eP62UWpPVIUe&I+8(zBfo?_F@87?N%g=9aE^cpjr*F^1 z?(_L?7yI)1YybG;m&}#e-Tt;awN1v~FW%i=?cbOz<@$Vo@#S`RYQ*kl4;OyQ-~YP1 zD&!m_e9CXjBI)1xg}M{F{k_ld6PZ7`zW2#nDe`}3_4BO%=S=fO=Z{~c`b9VQFDeSO zf9+D=E-eDD;JTX+=hqi?@MW;OpZ9ruA8+9em<3s9Az!%bxBGPeM)}Ho{aeuJty;8m z@Jb51SQYcD6L`_ytbd$kjjqbKPShe@5OQ(KJ#wjdJHIK*SAQ*EHH%GAghizQpTMp9 zcgtU~Uj1%&`qTO6n~S?Pt9Q+$e?7nb00zDMlK=YMAAkR+H_ggFmT7r&^5%#A)i2li zf)DSzh&EA|m*snxFdrc7FBkitFWwZ9H{fayWd8Z%=i7^Y77AA$6nb;J18whFQNVxA zg62*<=4SQ|ynp&g*AHC&;PbocP5%UiFF%7{|Kk#_1oeNs-CupWIIQ^(`}6A$AKzuw z>s1GJzbzAjj-jgV;2B?c|1KW8*gS;a%7D&r4E25GL;Iw@&Yx!g(;lQ1Cq9FFWR2Tf z`3-daeC@^I+0YLrN{$((UaEt*U8v5~3tMgiudO&Si(ZBFgAx2+g7hcDqDN3{fS4ZO zp-C@~hvvob@c%lOeaG@6W4Sqm<+~@q_o0xA#;n&GEv#gXp%W7!tez-Ey@(Y3B2vtY zNU<*>#l46W{~}Vt1}Sn$YF$#?B*`aDRJt;n`@p^;EqbbKRu&zx`?FsXEitG_h}1cQ zH<8`B?IL|5mDJRbRlQ*m**!fk<|QpM7)qqW^@2jT*I}_nlLL3uxGma6VF>7~q@tBH zfl^ZUsl<3tX)#XGqblJXz5`ml!yW27a8b@ zjHCngm{AxsxqejSJO)ytjyR9H1x~iLp$AO{qCr#Z7Bwcv8Exp8Hie;XG2=^vg$_bX z2sEaTxJAqg9FhicXky)hCHqVa1WGR^K8~+y3APJtp^_GD2!R=9B?+@0F$Wt8Ss)E! zA(4nP3=&YFj4RMhhB898jyQ!@8d5<+0$4{I-GU_KNv9RmlnW}2$Y3P8V4WiZyeJKo z$1pZ6NZeu*8Fax>NTVJ4v}r+NSEJaHZ77Xt#TX1@>*zTQDV(}$B|In zNKxpr1iR6CDH{WDNrHKdeh~(72qaE+?OrBM~b0~r{E~J(Y8N< z7piN1xv4ul^l_>f?s6&YTBuiBUa=}`AN46`u?08u|l^rR@wzaMY|be9bIyd zP2(xGtmOPiqO2Ulo^uObIw{IFMh}s5a-r8ki45xAw^K516`9Btr5*dRGEE}MuyF*I zm2davv{G`bOnSSsE2-30fsx!b9{Zh2>CHNlb)y~zS7x)pHR7=$XoJ%t^8VXrj~~|6 z&WrNznFn?sIaMYm+Vrmn8Xt^KOcX~7cf6ZVot3xnK8+e2w zc`GVqC=klgeV|)a(B(vbr5(20kp^9k+laRTG;mZQld01FB&c z<8q`l&I(t^D4vl~px7F<7M{@6zBF${W1N`)1SoWKutILtQ*Ommz1rC&J6L0^PzA)$ z+Yv)SeFhX)V2GYhEw>qKK5 zlRzO?==Pc`ibhchL{AlAVS$`87hvZ?w=apoCV@gb1~M)8ektNs)6_^b9`iQM}AxoH!vG!nvJOfjiu^poTEm9+AlGd z1rV-!3RgTJuBBdVuEUYD)PQ9{+74b;m3M%m8i=7X7fvw<TUluAa%c#5zQ5I53JEYPgP~X)&opO64rv0HZK`Lur@|J*lBahSi~F*uq(f9_o$Fi^hT&q7qaB zF;uDYJ9dGbReM1{n9+9Ap`b2MR0Ag#aD~NLYRI2jE zStbEBI6WT7$UsqzUWwWz`QjOsbJUO!u5x?08U@W2H7ZdzRiZ)FO0@9f&IYtCfub5= zsF9LrjML%K<()#GF&BuS%9o$v3*?*CfM0!xO1G&|e+eQQ>x!hIh|Lu~a@P8_ZW(L` zFA2*7IV}(Hha_R z6xDD;4Jm^_&Z@kiv#~i~4?3tHP#%I8$dA0O1+FozQG6I`80c+O&Z@j1%`=wK8Uh?B zsu4$iElW`3tjY^!!*W*hq7qah4JCrCNxtwSZ)(9pDUO9wkW`UrjG9~-h!Zp7By9V&QJXQFUU!2>apI57gi4;0mKLk*YeOcc&q7!>>rn>)kx zM_8hnyWnezcG#_D7BUH0EX2B!DH6$auLUVX>&-iSkl!~Cz z?G-sdmz`WRO)d(UQ8{Z{3w(o-TcL>81IDAAL)8i7tOO5;VK_S|-)?I~HPnbuXH?Eg z@UTbh+ZWWviO7IKMYf9oM&*;Nw=gY zHL6W`CUH>G7P`6EkrEn0g>^N|tcDTDkJ|@@Ge1C44g1X52vE5c`t&Yna}Ek$o6hL=-_FAAZBv7JfE&Vuvz?Vhqg@L-S3FPL z&w?7%aT;CUE?WDqbm9K33^lwuG)9P%$S8TBECAZ|m_XFXRsk}P@hI?7wcSUJhFu&z z>oK9IPF0V>S#2iBDT$F&9-L0-KO(O8*8(}G>VcL`V)W`QtPkw`YX_O;1ws_aIfW0T zdbc^%*Pkhi#=0g-&YA>){HSOA0X-%i)U$PnzMk-Z&Jq9qyXfi_%{h486)3+LjSUU4 zqUTsq=J~M#haL*uo>#yo_w=x{9?eI&CyMw7hd~cN>oH?#Td5e{N`=4ksx9OeJ4ecl zrK)Wl@Q!S7d(;p2wfe8a>)qRIsH2}8{A*+gkdU+XcqZ8%yM#}Rg6BsG ze4O6x{+Gj#Uef=ok6ya}n6JC>oTTSJs%|`nsk(-qAJugB*yESd_vbHtfBw>|J%1@* ec6q-4R3CJj|G3^?U6*OPyZaAS?`;)WbpQa%27|Ex literal 0 HcmV?d00001 diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index d9e5dba0a..1dbe16fbb 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -471,6 +471,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_bio_placeholder" = "Bio"; +"lng_collectible_username_title" = "{username} is a collectible username that belongs to"; +"lng_collectible_username_info" = "This username was bought on **Fragment** on {date} for {price}"; +"lng_collectible_username_copy" = "Copy Link"; +"lng_collectible_phone_title" = "{phone} is a collectible phone number that belongs to"; +"lng_collectible_phone_info" = "This phone number was bought on **Fragment** on {date} for {price}"; +"lng_collectible_phone_copy" = "Copy Phone Number"; +"lng_collectible_learn_more" = "Learn More"; + "lng_settings_section_info" = "My info"; "lng_settings_section_notify" = "Notifications"; diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc index b63b6d15f..5b72c461f 100644 --- a/Telegram/Resources/qrc/telegram/animations.qrc +++ b/Telegram/Resources/qrc/telegram/animations.qrc @@ -22,5 +22,7 @@ ../../animations/hours.tgs ../../animations/phone.tgs ../../animations/chat_link.tgs + ../../animations/collectible_username.tgs + ../../animations/collectible_phone.tgs diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index e53e7a5d7..b8919617d 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -796,7 +796,7 @@ QString ApiWrap::exportDirectStoryLink(not_null story) { const auto storyId = story->fullId(); const auto peer = story->peer(); const auto fallback = [&] { - const auto base = peer->userName(); + const auto base = peer->username(); const auto story = QString::number(storyId.story); const auto query = base + "/s/" + story; return session().createInternalLinkFull(query); diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 2ae5bba8d..f64f2103d 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -1044,3 +1044,33 @@ inviteForbiddenTitle: FlatLabel(boxTitle) { inviteForbiddenTitlePadding: margins(32px, 4px, 32px, 0px); inviteForbiddenLockBg: dialogsUnreadBgMuted; inviteForbiddenLockIcon: icon {{ "emoji/premium_lock", dialogsUnreadFg }}; + +collectibleIconDiameter: 72px; +collectibleIcon: 64px; +collectibleIconPadding: margins(24px, 32px, 24px, 12px); +collectibleHeader: FlatLabel(boxTitle) { + minWidth: 120px; + maxHeight: 0px; + align: align(top); +} +collectibleHeaderPadding: margins(24px, 16px, 24px, 12px); +collectibleOwnerPadding: margins(24px, 4px, 24px, 8px); +collectibleInfo: inviteForbiddenInfo; +collectibleInfoPadding: margins(24px, 12px, 24px, 12px); +collectibleInfoTonMargins: margins(0px, 3px, 0px, 0px); +collectibleMore: RoundButton(defaultActiveButton) { + height: 36px; + textTop: 9px; + radius: 6px; +} +collectibleMorePadding: margins(24px, 12px, 24px, 0px); +collectibleCopy: RoundButton(defaultLightButton) { + height: 36px; + textTop: 9px; + radius: 6px; +} +collectibleBox: Box(defaultBox) { + buttonPadding: margins(24px, 12px, 24px, 12px); + buttonHeight: 36px; + button: collectibleCopy; +} diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 2430c583e..149171e5b 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -1789,10 +1789,10 @@ crl::time PeerListContent::paintRow( if (row->isSearchResult() && !_mentionHighlight.isEmpty() && peer - && peer->userName().startsWith( + && peer->username().startsWith( _mentionHighlight, Qt::CaseInsensitive)) { - const auto username = peer->userName(); + const auto username = peer->username(); const auto availableWidth = statusw; auto highlightedPart = '@' + username.mid(0, _mentionHighlight.size()); auto grayedPart = username.mid(_mentionHighlight.size()); diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp index f8a7ea6e8..bae3d8a76 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp @@ -100,7 +100,7 @@ void Controller::prepare() { return; } auto row = std::make_unique(chat); - const auto username = chat->userName(); + const auto username = chat->username(); row->setCustomStatus(!username.isEmpty() ? ('@' + username) : (chat->isChannel() && !chat->isMegagroup()) diff --git a/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp b/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp index a6b442aca..6c19d4055 100644 --- a/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/prepare_short_info_box.cpp @@ -207,7 +207,7 @@ void ProcessFullPhoto( | UpdateFlag::Birthday) ) | rpl::map([=] { const auto user = peer->asUser(); - const auto username = peer->userName(); + const auto username = peer->username(); return PeerShortInfoFields{ .name = peer->name(), .phone = user ? Ui::FormatPhone(user->phone()) : QString(), diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp index 2a9c0d4b1..451957c21 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp @@ -322,7 +322,7 @@ void PublicsController::prepare() { auto &owner = _navigation->session().data(); for (const auto &chat : chats) { if (const auto peer = owner.processChat(chat)) { - if (!peer->isChannel() || peer->userName().isEmpty()) { + if (!peer->isChannel() || peer->username().isEmpty()) { continue; } appendRow(peer); @@ -346,7 +346,7 @@ void PublicsController::rowRightActionClicked(not_null row) { const auto text = textMethod( tr::now, lt_link, - peer->session().createInternalLink(peer->userName()), + peer->session().createInternalLink(peer->username()), lt_group, peer->name()); const auto confirmText = tr::lng_channels_too_much_public_revoke( @@ -389,7 +389,7 @@ std::unique_ptr PublicsController::createRow( auto result = std::make_unique(peer); result->setActionLink(tr::lng_channels_too_much_public_revoke(tr::now)); result->setCustomStatus( - _navigation->session().createInternalLink(peer->userName())); + _navigation->session().createInternalLink(peer->username())); return result; } diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 7ddee02d2..58c7af3a6 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -897,6 +897,34 @@ bool ShowEditPersonalChannel( return true; } +bool ShowCollectiblePhone( + Window::SessionController *controller, + const Match &match, + const QVariant &context) { + if (!controller) { + return false; + } + const auto phone = match->captured(1); + const auto peerId = PeerId(match->captured(2).toULongLong()); + controller->resolveCollectible( + peerId, + phone.startsWith('+') ? phone : '+' + phone); + return true; +} + +bool ShowCollectibleUsername( + Window::SessionController *controller, + const Match &match, + const QVariant &context) { + if (!controller) { + return false; + } + const auto username = match->captured(1); + const auto peerId = PeerId(match->captured(2).toULongLong()); + controller->resolveCollectible(peerId, username); + return true; +} + void ExportTestChatTheme( not_null controller, not_null theme) { @@ -1299,6 +1327,14 @@ const std::vector &InternalUrlHandlers() { u"^edit_personal_channel$"_q, ShowEditPersonalChannel, }, + { + u"^collectible_phone/([\\+0-9\\-\\s]+)@([0-9]+)$"_q, + ShowCollectiblePhone, + }, + { + u"^collectible_username/([a-zA-Z0-9\\-\\_\\.]+)@([0-9]+)$"_q, + ShowCollectibleUsername, + }, }; return Result; } diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index b32c85c53..b088be2d7 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -139,6 +139,10 @@ const std::vector &ChannelData::usernames() const { return _username.usernames(); } +bool ChannelData::isUsernameEditable(QString username) const { + return _username.isEditable(username); +} + void ChannelData::setAccessHash(uint64 accessHash) { access = accessHash; input = MTP_inputPeerChannel( diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index d22352020..6dbe84329 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -182,6 +182,7 @@ public: [[nodiscard]] QString username() const; [[nodiscard]] QString editableUsername() const; [[nodiscard]] const std::vector &usernames() const; + [[nodiscard]] bool isUsernameEditable(QString username) const; [[nodiscard]] int membersCount() const { return std::max(_membersCount, 1); diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 76389df4e..9b5d6d379 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -940,7 +940,7 @@ const QString &PeerData::shortName() const { return _name; } -QString PeerData::userName() const { +QString PeerData::username() const { if (const auto user = asUser()) { return user->username(); } else if (const auto channel = asChannel()) { @@ -949,6 +949,34 @@ QString PeerData::userName() const { return QString(); } +QString PeerData::editableUsername() const { + if (const auto user = asUser()) { + return user->editableUsername(); + } else if (const auto channel = asChannel()) { + return channel->editableUsername(); + } + return QString(); +} + +const std::vector &PeerData::usernames() const { + if (const auto user = asUser()) { + return user->usernames(); + } else if (const auto channel = asChannel()) { + return channel->usernames(); + } + static const auto kEmpty = std::vector(); + return kEmpty; +} + +bool PeerData::isUsernameEditable(QString username) const { + if (const auto user = asUser()) { + return user->isUsernameEditable(username); + } else if (const auto channel = asChannel()) { + return channel->isUsernameEditable(username); + } + return false; +} + bool PeerData::changeColorIndex(uint8 index) { index %= Ui::kColorIndexCount; if (_colorIndexCloud && _colorIndex == index) { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index bf7d4e025..b7f98e599 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -279,7 +279,11 @@ public: [[nodiscard]] const QString &name() const; [[nodiscard]] const QString &shortName() const; [[nodiscard]] const QString &topBarNameText() const; - [[nodiscard]] QString userName() const; + + [[nodiscard]] QString username() const; + [[nodiscard]] QString editableUsername() const; + [[nodiscard]] const std::vector &usernames() const; + [[nodiscard]] bool isUsernameEditable(QString username) const; [[nodiscard]] const base::flat_set &nameWords() const { return _nameWords; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 6bfd2b74e..4614fae09 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1230,7 +1230,7 @@ PeerData *Session::peerByUsername(const QString &username) const { const auto uname = username.trimmed(); for (const auto &[peerId, peer] : _peers) { if (peer->isLoaded() - && !peer->userName().compare(uname, Qt::CaseInsensitive)) { + && !peer->username().compare(uname, Qt::CaseInsensitive)) { return peer.get(); } } diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp index a856e6156..22cfbfeef 100644 --- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp +++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp @@ -303,7 +303,7 @@ void SponsoredMessages::append( ? _session->data().processBotApp(peerId, *data.vapp()) : nullptr; result.botLinkInfo = Window::PeerByLinkInfo{ - .usernameOrId = user->userName(), + .usernameOrId = user->username(), .resolveType = botAppData ? Window::ResolveType::BotApp : data.vstart_param() diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp index d4b12de6a..cc0fefe99 100644 --- a/Telegram/SourceFiles/data/data_story.cpp +++ b/Telegram/SourceFiles/data/data_story.cpp @@ -450,7 +450,7 @@ bool Story::hasDirectLink() const { if (!_privacyPublic || (!_pinned && expired())) { return false; } - return !_peer->userName().isEmpty(); + return !_peer->username().isEmpty(); } std::optional Story::errorTextForForward( diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index cffe8ad67..f4f8faccd 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -472,6 +472,10 @@ const std::vector &UserData::usernames() const { return _username.usernames(); } +bool UserData::isUsernameEditable(QString username) const { + return _username.isEditable(username); +} + const QString &UserData::phone() const { return _phone; } diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 7a8a22582..67da9e3f5 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -150,15 +150,11 @@ public: // a full check by canShareThisContact() call. [[nodiscard]] bool canShareThisContactFast() const; - MTPInputUser inputUser = MTP_inputUserEmpty(); - - QString firstName; - QString lastName; [[nodiscard]] const QString &phone() const; [[nodiscard]] QString username() const; [[nodiscard]] QString editableUsername() const; [[nodiscard]] const std::vector &usernames() const; - QString nameOrPhone; + [[nodiscard]] bool isUsernameEditable(QString username) const; enum class ContactStatus : char { Unknown, @@ -186,8 +182,6 @@ public: void setBirthday(Data::Birthday value); void setBirthday(const tl::conditional &value); - std::unique_ptr botInfo; - void setUnavailableReasons( std::vector &&reasons); @@ -209,6 +203,14 @@ public: [[nodiscard]] MsgId personalChannelMessageId() const; void setPersonalChannel(ChannelId channelId, MsgId messageId); + MTPInputUser inputUser = MTP_inputUserEmpty(); + + QString firstName; + QString lastName; + QString nameOrPhone; + + std::unique_ptr botInfo; + private: auto unavailableReasons() const -> const std::vector & override; diff --git a/Telegram/SourceFiles/data/data_user_names.cpp b/Telegram/SourceFiles/data/data_user_names.cpp index b69756b5e..c2edd6b01 100644 --- a/Telegram/SourceFiles/data/data_user_names.cpp +++ b/Telegram/SourceFiles/data/data_user_names.cpp @@ -80,4 +80,10 @@ const std::vector &UsernamesInfo::usernames() const { return _usernames; } +bool UsernamesInfo::isEditable(const QString &username) const { + return (_indexEditableUsername >= 0) + && (_indexEditableUsername < _usernames.size()) + && (_usernames[_indexEditableUsername] == username); +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_user_names.h b/Telegram/SourceFiles/data/data_user_names.h index 03853b520..04912ced9 100644 --- a/Telegram/SourceFiles/data/data_user_names.h +++ b/Telegram/SourceFiles/data/data_user_names.h @@ -27,6 +27,7 @@ public: [[nodiscard]] QString username() const; [[nodiscard]] QString editableUsername() const; [[nodiscard]] const std::vector &usernames() const; + [[nodiscard]] bool isEditable(const QString &username) const; private: std::vector _usernames; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 354a148eb..0da825209 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -1087,7 +1087,7 @@ void InnerWidget::paintPeerSearchResult( QRect tr(context.st->textLeft, context.st->textTop, namewidth, st::dialogsTextFont->height); p.setFont(st::dialogsTextFont); - QString username = peer->userName(); + QString username = peer->username(); if (!context.active && username.startsWith(_peerSearchQuery, Qt::CaseInsensitive)) { auto first = '@' + username.mid(0, _peerSearchQuery.size()); auto second = username.mid(_peerSearchQuery.size()); @@ -4021,7 +4021,7 @@ void InnerWidget::setupShortcuts() { const auto history = thread->owningHistory(); const auto isArchived = history->folder() && (history->folder()->id() == Data::Folder::kId); - + Window::ToggleHistoryArchived( _controller->uiShow(), history, diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index ab1f19958..5899117be 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -496,7 +496,7 @@ auto GenerateParticipantString( data, }); } - const auto username = peer->userName(); + const auto username = peer->username(); if (username.isEmpty()) { return name; } diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp index 1a0cedb1b..494f58f9a 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp @@ -217,7 +217,18 @@ void AddRecipient(not_null box, const TextWithEntities &t) { } #endif -[[nodiscard]] QImage IconCurrency( +[[nodiscard]] QString FormatDate(const QDateTime &date) { + return tr::lng_group_call_starts_short_date( + tr::now, + lt_date, + langDayOfMonth(date.date()), + lt_time, + QLocale().toString(date.time(), QLocale::ShortFormat)); +} + +} // namespace + +QImage IconCurrency( const style::FlatLabel &label, const QColor &c) { const auto s = Size(label.style.font->ascent); @@ -234,17 +245,6 @@ void AddRecipient(not_null box, const TextWithEntities &t) { return image; } -[[nodiscard]] QString FormatDate(const QDateTime &date) { - return tr::lng_group_call_starts_short_date( - tr::now, - lt_date, - langDayOfMonth(date.date()), - lt_time, - QLocale().toString(date.time(), QLocale::ShortFormat)); -} - -} // namespace - InnerWidget::InnerWidget( QWidget *parent, not_null controller, diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.h b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.h index 69d1587d8..99e9511be 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.h +++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.h @@ -23,6 +23,10 @@ namespace Info::ChannelEarn { class Memento; +[[nodiscard]] QImage IconCurrency( + const style::FlatLabel &label, + const QColor &c); + class InnerWidget final : public Ui::VerticalLayout { public: struct ShowRequest final { diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index 19d7eebae..84d8daba3 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -116,16 +116,25 @@ base::options::toggle ShowPeerIdBelowAbout({ [[nodiscard]] Fn UsernamesLinkCallback( not_null peer, - std::shared_ptr show, + not_null controller, const QString &addToLink) { + const auto weak = base::make_weak(controller); return [=](QString link) { - if (!link.startsWith(u"https://"_q)) { - link = peer->session().createInternalLinkFull(peer->userName()) + if (link.startsWith(u"internal:"_q)) { + Core::App().openInternalUrl(link, + QVariant::fromValue(ClickHandlerContext{ + .sessionWindow = weak, + })); + return; + } else if (!link.startsWith(u"https://"_q)) { + link = peer->session().createInternalLinkFull(peer->username()) + addToLink; } if (!link.isEmpty()) { QGuiApplication::clipboard()->setText(link); - show->showToast(tr::lng_username_copied(tr::now)); + if (const auto window = weak.get()) { + window->showToast(tr::lng_username_copied(tr::now)); + } } }; } @@ -1041,16 +1050,13 @@ object_ptr DetailsFiller::setupInfo() { UsernameValue(user, true) | rpl::map([=](TextWithEntities u) { return u.text.isEmpty() ? TextWithEntities() - : Ui::Text::Link( - u, - user->session().createInternalLinkFull( - u.text.mid(1))); + : Ui::Text::Link(u, UsernameUrl(user, u.text.mid(1))); }), QString(), st::infoProfileLabeledUsernamePadding); const auto callback = UsernamesLinkCallback( _peer, - controller->uiShow(), + controller, QString()); const auto hook = [=](Ui::FlatLabel::ContextMenuRequest request) { if (!request.link) { @@ -1094,7 +1100,7 @@ object_ptr DetailsFiller::setupInfo() { }, copyUsername->lifetime()); copyUsername->setClickedCallback([=] { const auto link = user->session().createInternalLinkFull( - user->userName()); + user->username()); if (!link.isEmpty()) { QGuiApplication::clipboard()->setText(link); controller->showToast(tr::lng_username_copied(tr::now)); @@ -1159,7 +1165,7 @@ object_ptr DetailsFiller::setupInfo() { const auto controller = _controller->parentController(); const auto linkCallback = UsernamesLinkCallback( _peer, - controller->uiShow(), + controller, addToLink); linkLine.text->overrideLinkClickHandler(linkCallback); linkLine.subtext->overrideLinkClickHandler(linkCallback); diff --git a/Telegram/SourceFiles/info/profile/info_profile_phone_menu.cpp b/Telegram/SourceFiles/info/profile/info_profile_phone_menu.cpp index 58c51ed31..85fde827f 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_phone_menu.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_phone_menu.cpp @@ -112,23 +112,22 @@ int TextItem::contentHeight() const { } // namespace -void AddPhoneMenu(not_null menu, not_null user) { - if (user->isSelf()) { - return; - } +bool IsCollectiblePhone(not_null user) { using Strings = std::vector; const auto prefixes = user->session().appConfig().get( u"fragment_prefixes"_q, - std::vector()); - { - const auto proj = [&phone = user->phone()](const QString &p) { - return phone.startsWith(p); - }; - if (ranges::none_of(prefixes, proj)) { - return; - } - } - if (const auto url = AppConfig::FragmentLink(&user->session())) { + Strings{ u"888"_q }); + const auto phone = user->phone(); + const auto proj = [&](const QString &p) { + return phone.startsWith(p); + }; + return ranges::any_of(prefixes, proj); +} + +void AddPhoneMenu(not_null menu, not_null user) { + if (user->isSelf() || !IsCollectiblePhone(user)) { + return; + } else if (const auto url = AppConfig::FragmentLink(&user->session())) { menu->addSeparator(&st::expandedMenuSeparator); const auto link = Ui::Text::Link( tr::lng_info_mobile_context_menu_fragment_about_link(tr::now), diff --git a/Telegram/SourceFiles/info/profile/info_profile_phone_menu.h b/Telegram/SourceFiles/info/profile/info_profile_phone_menu.h index 82ed6bb8c..25e6c8b5c 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_phone_menu.h +++ b/Telegram/SourceFiles/info/profile/info_profile_phone_menu.h @@ -16,6 +16,8 @@ class PopupMenu; namespace Info { namespace Profile { +[[nodiscard]] bool IsCollectiblePhone(not_null user); + void AddPhoneMenu(not_null menu, not_null user); } // namespace Profile diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 9fe23da1f..6159fe907 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_chat_participants.h" #include "apiwrap.h" +#include "info/profile/info_profile_phone_menu.h" #include "info/profile/info_profile_badge.h" #include "core/application.h" #include "core/click_handler_types.h" @@ -55,7 +56,7 @@ auto PlainUsernameValue(not_null peer) { peer->session().changes().peerFlagsValue(peer, UpdateFlag::Username), peer->session().changes().peerFlagsValue(peer, UpdateFlag::Usernames) ) | rpl::map([=] { - return peer->userName(); + return peer->username(); }); } @@ -136,14 +137,19 @@ rpl::producer PhoneOrHiddenValue(not_null user) { PlainUsernameValue(user), PlainAboutValue(user), tr::lng_info_mobile_hidden() - ) | rpl::map([]( + ) | rpl::map([user]( const TextWithEntities &phone, const QString &username, const QString &about, const QString &hidden) { - return (phone.text.isEmpty() && username.isEmpty() && about.isEmpty()) - ? Ui::Text::WithEntities(hidden) - : phone; + if (phone.text.isEmpty() && username.isEmpty() && about.isEmpty()) { + return Ui::Text::WithEntities(hidden); + } else if (IsCollectiblePhone(user)) { + return Ui::Text::Link(phone, u"internal:collectible_phone/"_q + + user->phone() + '@' + QString::number(user->id.value)); + } else { + return phone; + } }); } @@ -160,15 +166,22 @@ rpl::producer UsernameValue( }) | Ui::Text::ToWithEntities(); } +QString UsernameUrl(not_null peer, const QString &username) { + return peer->isUsernameEditable(username) + ? peer->session().createInternalLinkFull(username) + : (u"internal:collectible_username/"_q + + username + + "@" + + QString::number(peer->id.value)); +} + rpl::producer> UsernamesValue( not_null peer) { const auto map = [=](const std::vector &usernames) { return ranges::views::all( usernames ) | ranges::views::transform([&](const QString &u) { - return Ui::Text::Link( - u, - peer->session().createInternalLinkFull(u)); + return Ui::Text::Link(u, UsernameUrl(peer, u)); }) | ranges::to_vector; }; auto value = rpl::merge( @@ -224,9 +237,7 @@ rpl::producer LinkValue(not_null peer, bool primary) { ? PlainPrimaryUsernameValue(peer) : PlainUsernameValue(peer) | rpl::type_erased() ) | rpl::map([=](QString &&username) { - return username.isEmpty() - ? QString() - : peer->session().createInternalLinkFull(username); + return username.isEmpty() ? QString() : UsernameUrl(peer, username); }); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index 6b7ccf475..625ccfa2a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -61,6 +61,9 @@ rpl::producer> MigratedOrMeValue( bool primary = false); [[nodiscard]] rpl::producer> UsernamesValue( not_null peer); +[[nodiscard]] QString UsernameUrl( + not_null peer, + const QString &username); [[nodiscard]] TextWithEntities AboutWithEntities( not_null peer, const QString &value); diff --git a/Telegram/SourceFiles/main/main_domain.cpp b/Telegram/SourceFiles/main/main_domain.cpp index cc6bd9457..aafbcd2ac 100644 --- a/Telegram/SourceFiles/main/main_domain.cpp +++ b/Telegram/SourceFiles/main/main_domain.cpp @@ -53,7 +53,7 @@ Domain::Domain(const QString &dataName) : rpl::never(); }) | rpl::flatten_latest( ) | rpl::start_with_next([](const Data::PeerUpdate &update) { - CrashReports::SetAnnotation("Username", update.peer->userName()); + CrashReports::SetAnnotation("Username", update.peer->username()); }, _lifetime); } diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index ffa288223..896a38a6b 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -224,7 +224,7 @@ void Cover::initViewers() { }, lifetime()); _username->overrideLinkClickHandler([=] { - const auto username = _user->userName(); + const auto username = _user->username(); if (username.isEmpty()) { _controller->show(Box(UsernamesBox, _user)); } else { diff --git a/Telegram/SourceFiles/ui/boxes/collectible_info_box.cpp b/Telegram/SourceFiles/ui/boxes/collectible_info_box.cpp new file mode 100644 index 000000000..4498becad --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/collectible_info_box.cpp @@ -0,0 +1,271 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/boxes/collectible_info_box.h" + +#include "base/unixtime.h" +#include "core/file_utilities.h" +#include "lang/lang_keys.h" +#include "lottie/lottie_icon.h" +#include "info/channel_statistics/earn/earn_format.h" +#include "ui/layers/generic_box.h" +#include "ui/text/format_values.h" +#include "ui/text/text_utilities.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/dynamic_image.h" +#include "ui/painter.h" +#include "settings/settings_common.h" +#include "styles/style_boxes.h" +#include "styles/style_layers.h" + +#include +#include + +namespace Ui { +namespace { + +constexpr auto kTonMultiplier = uint64(1000000000); + +[[nodiscard]] QString FormatEntity(CollectibleType type, QString entity) { + switch (type) { + case CollectibleType::Phone: { + static const auto kNonDigits = QRegularExpression(u"[^\\d]"_q); + entity.replace(kNonDigits, QString()); + } return Ui::FormatPhone(entity); + case CollectibleType::Username: + return entity.startsWith('@') ? entity : ('@' + entity); + } + Unexpected("CollectibleType in FormatEntity."); +} + +[[nodiscard]] QString FormatDate(TimeId date) { + return langDateTime(base::unixtime::parse(date)); +} + +[[nodiscard]] TextWithEntities FormatPrice( + const CollectibleInfo &info, + const CollectibleDetails &details) { + auto minor = Info::ChannelEarn::MinorPart(info.cryptoAmount); + if (minor.size() == 1 && minor.at(0) == '.') { + minor += '0'; + } + auto price = (info.cryptoCurrency == u"TON"_q) + ? base::duplicate( + details.tonEmoji + ).append( + Info::ChannelEarn::MajorPart(info.cryptoAmount) + ).append(minor) + : TextWithEntities{ ('{' + + info.cryptoCurrency + ':' + QString::number(info.cryptoAmount) + + '}') }; + const auto fiat = Ui::FillAmountAndCurrency(info.amount, info.currency); + return Ui::Text::Wrapped( + price, + EntityType::Bold + ).append(u" ("_q + fiat + ')'); +} + +[[nodiscard]] object_ptr MakeOwnerCell( + not_null parent, + const CollectibleInfo &info) { + const auto st = &st::defaultMultiSelectItem; + const auto size = st->height; + auto result = object_ptr(parent.get(), size); + const auto raw = result.data(); + + const auto name = info.ownerName; + const auto userpic = info.ownerUserpic; + const auto nameWidth = st->style.font->width(name); + const auto added = size + st->padding.left() + st->padding.right(); + const auto subscribed = std::make_shared(false); + raw->paintRequest() | rpl::start_with_next([=] { + const auto use = std::min(nameWidth + added, raw->width()); + const auto x = (raw->width() - use) / 2; + if (const auto available = use - added; available > 0) { + auto p = QPainter(raw); + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(st->textBg); + p.drawRoundedRect(x, 0, use, size, size / 2., size / 2.); + + if (!*subscribed) { + *subscribed = true; + userpic->subscribeToUpdates([=] { raw->update(); }); + } + p.drawImage(QRect(x, 0, size, size), userpic->image(size)); + + const auto textx = x + size + st->padding.left(); + const auto texty = st->padding.top() + st->style.font->ascent; + const auto text = (use == nameWidth + added) + ? name + : st->style.font->elided(name, available); + p.setPen(st->textFg); + p.setFont(st->style.font); + p.drawText(textx, texty, text); + } + }, raw->lifetime()); + + return result; +} + +} // namespace + +CollectibleType DetectCollectibleType(const QString &entity) { + return entity.startsWith('+') + ? CollectibleType::Phone + : CollectibleType::Username; +} + +void CollectibleInfoBox( + not_null box, + CollectibleInfo info, + CollectibleDetails details) { + box->setWidth(st::boxWideWidth); + box->setStyle(st::collectibleBox); + + const auto type = DetectCollectibleType(info.entity); + + const auto icon = box->addRow( + object_ptr(box, st::collectibleIconDiameter), + st::collectibleIconPadding); + icon->paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + const auto size = icon->height(); + const auto inner = QRect( + (icon->width() - size) / 2, + 0, + size, + size); + if (!inner.intersects(clip)) { + return; + } + auto p = QPainter(icon); + auto hq = PainterHighQualityEnabler(p); + p.setBrush(st::defaultActiveButton.textBg); + p.setPen(Qt::NoPen); + p.drawEllipse(inner); + }, icon->lifetime()); + const auto lottieSize = st::collectibleIcon; + auto lottie = Settings::CreateLottieIcon( + icon, + { + .name = (type == CollectibleType::Phone + ? u"collectible_phone"_q + : u"collectible_username"_q), + .color = &st::defaultActiveButton.textFg, + .sizeOverride = { lottieSize, lottieSize }, + }, + QMargins()); + box->showFinishes( + ) | rpl::start_with_next([animate = std::move(lottie.animate)] { + animate(anim::repeat::once); + }, box->lifetime()); + const auto animation = lottie.widget.release(); + icon->sizeValue() | rpl::start_with_next([=](QSize size) { + const auto skip = (type == CollectibleType::Phone) + ? style::ConvertScale(2) + : 0; + animation->move( + (size.width() - animation->width()) / 2, + skip + (size.height() - animation->height()) / 2); + }, animation->lifetime()); + + const auto formatted = FormatEntity(type, info.entity); + const auto header = (type == CollectibleType::Phone) + ? tr::lng_collectible_phone_title( + tr::now, + lt_phone, + Ui::Text::Link(formatted), + Ui::Text::WithEntities) + : tr::lng_collectible_username_title( + tr::now, + lt_username, + Ui::Text::Link(formatted), + Ui::Text::WithEntities); + const auto copyCallback = [box, type, formatted, text = info.copyText] { + QGuiApplication::clipboard()->setText( + text.isEmpty() ? formatted : text); + box->uiShow()->showToast((type == CollectibleType::Phone) + ? tr::lng_text_copied(tr::now) + : tr::lng_username_copied(tr::now)); + }; + box->addRow( + object_ptr( + box, + rpl::single(header), + st::collectibleHeader), + st::collectibleHeaderPadding + )->setClickHandlerFilter([copyCallback](const auto &...) { + copyCallback(); + return false; + }); + + box->addRow(MakeOwnerCell(box, info), st::collectibleOwnerPadding); + + const auto text = ((type == CollectibleType::Phone) + ? tr::lng_collectible_phone_info + : tr::lng_collectible_username_info)( + tr::now, + lt_date, + TextWithEntities{ FormatDate(info.date) }, + lt_price, + FormatPrice(info, details), + Ui::Text::RichLangValue); + const auto label = box->addRow( + object_ptr(box, st::collectibleInfo), + st::collectibleInfoPadding); + label->setAttribute(Qt::WA_TransparentForMouseEvents); + label->setMarkedText(text, details.tonEmojiContext()); + + const auto more = box->addRow( + object_ptr( + box, + tr::lng_collectible_learn_more(), + st::collectibleMore), + st::collectibleMorePadding); + more->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); + more->setClickedCallback([url = info.url] { + File::OpenUrl(url); + }); + + const auto phrase = (type == CollectibleType::Phone) + ? tr::lng_collectible_phone_copy + : tr::lng_collectible_username_copy; + auto owned = object_ptr( + box, + phrase(), + st::collectibleCopy); + const auto copy = owned.data(); + copy->setTextTransform(Ui::RoundButton::TextTransform::NoTransform); + copy->setClickedCallback(copyCallback); + box->addButton(std::move(owned)); + + box->setNoContentMargin(true); + const auto buttonsParent = box->verticalLayout().get(); + const auto close = Ui::CreateChild( + buttonsParent, + st::boxTitleClose); + close->setClickedCallback([=] { + box->closeBox(); + }); + box->widthValue( + ) | rpl::start_with_next([=](int width) { + close->moveToRight(0, 0); + }, box->lifetime()); + + box->widthValue() | rpl::start_with_next([=](int width) { + more->setFullWidth(width + - st::collectibleMorePadding.left() + - st::collectibleMorePadding.right()); + copy->setFullWidth(width + - st::collectibleBox.buttonPadding.left() + - st::collectibleBox.buttonPadding.right()); + }, box->lifetime()); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/boxes/collectible_info_box.h b/Telegram/SourceFiles/ui/boxes/collectible_info_box.h new file mode 100644 index 000000000..8b80b9650 --- /dev/null +++ b/Telegram/SourceFiles/ui/boxes/collectible_info_box.h @@ -0,0 +1,45 @@ +/* +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 Ui { + +class GenericBox; +class DynamicImage; + +enum class CollectibleType { + Phone, + Username, +}; + +[[nodiscard]] CollectibleType DetectCollectibleType(const QString &entity); + +struct CollectibleInfo { + QString entity; + QString copyText; + std::shared_ptr ownerUserpic; + QString ownerName; + uint64 cryptoAmount = 0; + uint64 amount = 0; + QString cryptoCurrency; + QString currency; + QString url; + TimeId date = 0; +}; + +struct CollectibleDetails { + TextWithEntities tonEmoji; + Fn tonEmojiContext; +}; + +void CollectibleInfoBox( + not_null box, + CollectibleInfo info, + CollectibleDetails details); + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/text/format_values.cpp b/Telegram/SourceFiles/ui/text/format_values.cpp index dec5eb312..41e985723 100644 --- a/Telegram/SourceFiles/ui/text/format_values.cpp +++ b/Telegram/SourceFiles/ui/text/format_values.cpp @@ -357,6 +357,7 @@ CurrencyRule LookupCurrencyRule(const QString ¤cy) { char do_decimal_point() const override { return decimal; } char do_thousands_sep() const override { return thousands; } + std::string do_grouping() const override { return "\3"; } char decimal = '.'; char thousands = ','; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 0276c3771..d5c2d3fce 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -135,8 +135,8 @@ not_null AddMyChannelsBox( const auto count = c ? c->membersCount() : g->count; _status.setText( st::defaultTextStyle, - !p->userName().isEmpty() - ? ('@' + p->userName()) + !p->username().isEmpty() + ? ('@' + p->username()) : count ? tr::lng_chat_status_subscribers( tr::now, diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 18c536cf3..86a5f53c1 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/delete_messages_box.h" #include "window/window_controller.h" #include "window/window_filters_menu.h" +#include "info/channel_statistics/earn/info_earn_inner_widget.h" #include "info/info_memento.h" #include "info/info_controller.h" #include "inline_bots/bot_attach_web_view.h" @@ -26,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_scheduled_section.h" #include "media/player/media_player_instance.h" #include "media/view/media_view_open_common.h" +#include "data/stickers/data_custom_emoji.h" #include "data/data_document_resolver.h" #include "data/data_download_manager.h" #include "data/data_session.h" @@ -49,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/shortcuts.h" #include "core/application.h" #include "core/click_handler_types.h" +#include "core/ui_integration.h" #include "base/unixtime.h" #include "ui/controls/userpic_button.h" #include "ui/text/text_utilities.h" @@ -62,7 +65,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "calls/calls_instance.h" // Core::App().calls().inCall(). #include "calls/group/calls_group_call.h" #include "ui/boxes/calendar_box.h" +#include "ui/boxes/collectible_info_box.h" #include "ui/boxes/confirm_box.h" +#include "ui/dynamic_thumbnails.h" #include "mainwidget.h" #include "main/main_app_config.h" #include "main/main_domain.h" @@ -83,6 +88,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_premium.h" #include "settings/settings_privacy_security.h" #include "styles/style_window.h" +#include "styles/style_boxes.h" #include "styles/style_dialogs.h" #include "styles/style_layers.h" // st::boxLabel @@ -155,6 +161,47 @@ private: return false; } +[[nodiscard]] Ui::CollectibleDetails PrepareCollectibleDetails( + not_null session) { + const auto makeContext = [=] { + return Core::MarkedTextContext{ + .session = session, + .customEmojiRepaint = [] {}, + }; + }; + return { + .tonEmoji = Ui::Text::SingleCustomEmoji( + session->data().customEmojiManager().registerInternalEmoji( + Info::ChannelEarn::IconCurrency( + st::collectibleInfo, + st::collectibleInfo.textFg->c), + st::collectibleInfoTonMargins, + true)), + .tonEmojiContext = makeContext, + }; +} + +[[nodiscard]] Ui::CollectibleInfo Parse( + const QString &entity, + not_null owner, + const MTPfragment_CollectibleInfo &info) { + const auto &data = info.data(); + return { + .entity = entity, + .copyText = (entity.startsWith('+') + ? QString() + : owner->session().createInternalLinkFull(entity)), + .ownerUserpic = Ui::MakeUserpicThumbnail(owner, true), + .ownerName = owner->name(), + .cryptoAmount = data.vcrypto_amount().v, + .amount = data.vamount().v, + .cryptoCurrency = qs(data.vcrypto_currency()), + .currency = qs(data.vcurrency()), + .url = qs(data.vurl()), + .date = data.vpurchase_date().v, + }; +} + MainWindowShow::MainWindowShow(not_null controller) : _window(base::make_weak(controller)) { } @@ -688,6 +735,36 @@ void SessionNavigation::resolveBoostState(not_null channel) { }).send(); } +void SessionNavigation::resolveCollectible( + PeerId ownerId, + const QString &entity, + Fn fail) { + if (_collectibleEntity == entity) { + return; + } else { + _api.request(base::take(_collectibleRequestId)).cancel(); + } + _collectibleEntity = entity; + _collectibleRequestId = _api.request(MTPfragment_GetCollectibleInfo( + ((Ui::DetectCollectibleType(entity) == Ui::CollectibleType::Phone) + ? MTP_inputCollectiblePhone(MTP_string(entity)) + : MTP_inputCollectibleUsername(MTP_string(entity))) + )).done([=](const MTPfragment_CollectibleInfo &result) { + const auto entity = base::take(_collectibleEntity); + _collectibleRequestId = 0; + uiShow()->show(Box( + Ui::CollectibleInfoBox, + Parse(entity, _session->data().peer(ownerId), result), + PrepareCollectibleDetails(_session))); + }).fail([=](const MTP::Error &error) { + _collectibleEntity = QString(); + _collectibleRequestId = 0; + if (fail) { + fail(error.type()); + } + }).send(); +} + void SessionNavigation::applyBoost( not_null channel, Fn done) { diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 811fb87a3..293dda20c 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -245,6 +245,11 @@ public: void resolveBoostState(not_null channel); + void resolveCollectible( + PeerId ownerId, + const QString &entity, + Fn fail = nullptr); + base::weak_ptr showToast( Ui::Toast::Config &&config); base::weak_ptr showToast( @@ -304,6 +309,9 @@ private: ChannelData *_boostStateResolving = nullptr; + QString _collectibleEntity; + mtpRequestId _collectibleRequestId = 0; + }; class SessionController : public SessionNavigation { diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 3cd0defa7..3a8f8ef34 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -240,6 +240,8 @@ PRIVATE ui/boxes/choose_language_box.h ui/boxes/choose_time.cpp ui/boxes/choose_time.h + ui/boxes/collectible_info_box.cpp + ui/boxes/collectible_info_box.h ui/boxes/confirm_box.cpp ui/boxes/confirm_box.h ui/boxes/confirm_phone_box.cpp From 1e0002aa7490faacf0afef508e316fa8cca7fee7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 23:09:26 +0400 Subject: [PATCH 15/71] Fix jump to original from story repost. --- .../SourceFiles/media/stories/media_stories_controller.cpp | 7 +++++++ .../SourceFiles/media/stories/media_stories_controller.h | 1 + .../media/stories/media_stories_repost_view.cpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index a56e10252..d43ff3a27 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -933,6 +933,13 @@ void Controller::show( peer->updateFull(); } +void Controller::jumpTo( + not_null story, + Data::StoriesContext context) { + show(story, std::move(context)); + _delegate->storiesRedisplay(story); +} + bool Controller::changeShown(Data::Story *story) { const auto id = story ? story->fullId() : FullStoryId(); const auto session = story ? &story->session() : nullptr; diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index b8ad1e20b..b8745d4ed 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -141,6 +141,7 @@ public: -> HistoryView::Reactions::CachedIconFactory &; void show(not_null story, Data::StoriesContext context); + void jumpTo(not_null story, Data::StoriesContext context); void ready(); void updateVideoPlayback(const Player::TrackState &state); diff --git a/Telegram/SourceFiles/media/stories/media_stories_repost_view.cpp b/Telegram/SourceFiles/media/stories/media_stories_repost_view.cpp index dd5c55ea7..4dfdd6a56 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_repost_view.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_repost_view.cpp @@ -178,7 +178,7 @@ RepostClickHandler RepostView::lookupHandler(QPoint position) { const auto of = owner->stories().lookup({ peer->id, id }); if (of) { using namespace Data; - _controller->show(*of, { StoriesContextSingle() }); + _controller->jumpTo(*of, { StoriesContextSingle() }); } else { _controller->uiShow()->show(PrepareShortInfoBox(peer)); } From ae36019ba99131df31a2e7056da720b3bad1b827 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 Apr 2024 23:13:05 +0400 Subject: [PATCH 16/71] Version 4.16.1. - Show information about Fragment phone numbers and usernames. - Fix jump to original story from a story repost. - Fix old image display in viewer in some cases. - Fix several crashes. --- 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 | 7 +++++++ 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 2627ef790..d97818b8e 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.16.1.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 09f5f8a95..7e6c0165e 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,0,0 - PRODUCTVERSION 4,16,0,0 + FILEVERSION 4,16,1,0 + PRODUCTVERSION 4,16,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.16.0.0" + VALUE "FileVersion", "4.16.1.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.0.0" + VALUE "ProductVersion", "4.16.1.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 95a5053d0..4b7bc1131 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,0,0 - PRODUCTVERSION 4,16,0,0 + FILEVERSION 4,16,1,0 + PRODUCTVERSION 4,16,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "4.16.0.0" + VALUE "FileVersion", "4.16.1.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.0.0" + VALUE "ProductVersion", "4.16.1.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index e3374144e..027295e4f 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 = 4016000; -constexpr auto AppVersionStr = "4.16"; +constexpr auto AppVersion = 4016001; +constexpr auto AppVersionStr = "4.16.1"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 7da53e563..91212612f 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4016000 +AppVersion 4016001 AppVersionStrMajor 4.16 -AppVersionStrSmall 4.16 -AppVersionStr 4.16.0 +AppVersionStrSmall 4.16.1 +AppVersionStr 4.16.1 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 4.16 +AppVersionOriginal 4.16.1 diff --git a/changelog.txt b/changelog.txt index a0e595060..56ff2649b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +4.16.1 (02.04.24) + +- Show information about Fragment phone numbers and usernames. +- Fix jump to original story from a story repost. +- Fix old image display in viewer in some cases. +- Fix several crashes. + 4.16 (01.04.24) - Instant View on Windows 10+ and macOS. From 91f8989f701c1c3bfe299ac8e653efc7d22a38e3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 3 Apr 2024 11:59:33 +0400 Subject: [PATCH 17/71] Remove SendMediaReady legacy helper. --- Telegram/SourceFiles/api/api_peer_photo.cpp | 33 ++-- Telegram/SourceFiles/api/api_ringtones.cpp | 33 ++-- Telegram/SourceFiles/api/api_sending.cpp | 2 +- Telegram/SourceFiles/api/api_sending.h | 4 +- .../boxes/background_preview_box.cpp | 4 +- Telegram/SourceFiles/history/history_widget.h | 1 - Telegram/SourceFiles/iv/iv_data.cpp | 2 +- Telegram/SourceFiles/mainwidget.h | 1 - .../passport/passport_form_controller.cpp | 12 +- Telegram/SourceFiles/storage/file_upload.cpp | 177 +++++------------- Telegram/SourceFiles/storage/file_upload.h | 6 +- .../SourceFiles/storage/localimageloader.cpp | 83 +++----- .../SourceFiles/storage/localimageloader.h | 74 +++----- .../window/themes/window_theme.cpp | 41 ++-- .../SourceFiles/window/themes/window_theme.h | 4 +- .../window/themes/window_theme_editor_box.cpp | 60 +++--- 16 files changed, 183 insertions(+), 354 deletions(-) diff --git a/Telegram/SourceFiles/api/api_peer_photo.cpp b/Telegram/SourceFiles/api/api_peer_photo.cpp index c70ef4491..9c8a470fa 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.cpp +++ b/Telegram/SourceFiles/api/api_peer_photo.cpp @@ -34,7 +34,7 @@ namespace { constexpr auto kSharedMediaLimit = 100; -[[nodiscard]] SendMediaReady PreparePeerPhoto( +[[nodiscard]] std::shared_ptr PreparePeerPhoto( MTP::DcId dcId, PeerId peerId, QImage &&image) { @@ -80,24 +80,17 @@ constexpr auto kSharedMediaLimit = 100; MTPVector(), MTP_int(dcId)); - QString file, filename; - int64 filesize = 0; - QByteArray data; - - return SendMediaReady( - SendMediaType::Photo, - file, - filename, - filesize, - data, - id, - id, - u"jpg"_q, - peerId, - photo, - photoThumbs, - MTP_documentEmpty(MTP_long(0)), - jpeg); + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::Photo, + }); + result->type = SendMediaType::Photo; + result->setFileData(jpeg); + result->thumbId = id; + result->thumbname = "thumb.jpg"; + result->photo = photo; + result->photoThumbs = photoThumbs; + return result; } [[nodiscard]] std::optional PrepareMtpMarkup( @@ -239,7 +232,7 @@ void PeerPhoto::upload( _api.instance().mainDcId(), peer->id, base::take(photo.image)); - _session->uploader().uploadMedia(fakeId, ready); + _session->uploader().upload(fakeId, ready); } } diff --git a/Telegram/SourceFiles/api/api_ringtones.cpp b/Telegram/SourceFiles/api/api_ringtones.cpp index 87522bf58..307ba580a 100644 --- a/Telegram/SourceFiles/api/api_ringtones.cpp +++ b/Telegram/SourceFiles/api/api_ringtones.cpp @@ -24,16 +24,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Api { namespace { -SendMediaReady PrepareRingtoneDocument( +std::shared_ptr PrepareRingtoneDocument( MTP::DcId dcId, const QString &filename, const QString &filemime, const QByteArray &content) { + const auto id = base::RandomValue(); auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - const auto id = base::RandomValue(); - const auto document = MTP_document( + + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::File, + }); + result->filename = filename; + result->content = content; + result->filesize = content.size(); + result->setFileData(content); + result->document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -45,21 +54,7 @@ SendMediaReady PrepareRingtoneDocument( MTPVector(), MTP_int(dcId), MTP_vector(std::move(attributes))); - - return SendMediaReady( - SendMediaType::File, - QString(), // filepath - filename, - content.size(), - content, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - PreparedPhotoThumbs(), - document, - QByteArray()); + return result; } } // namespace @@ -102,7 +97,7 @@ void Ringtones::upload( _uploads.erase(already); } _uploads.emplace(fakeId, uploadedData); - _session->uploader().uploadMedia(fakeId, ready); + _session->uploader().upload(fakeId, ready); } void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) { diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 72bd06b3d..d91943fe0 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -353,7 +353,7 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file) { + const std::shared_ptr &file) { const auto isEditing = (file->type != SendMediaType::Audio) && (file->to.replaceMediaOf != 0); const auto newId = FullMsgId( diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h index e17c66f3e..2fdbad843 100644 --- a/Telegram/SourceFiles/api/api_sending.h +++ b/Telegram/SourceFiles/api/api_sending.h @@ -14,7 +14,7 @@ class Session; class History; class PhotoData; class DocumentData; -struct FileLoadResult; +struct FilePrepareResult; namespace Api { @@ -40,6 +40,6 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file); + const std::shared_ptr &file); } // namespace Api diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index 3a9917719..a112c478a 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -593,11 +593,11 @@ void BackgroundPreviewBox::uploadForPeer(bool both) { const auto ready = Window::Theme::PrepareWallPaper( session->mainDcId(), _paper.localThumbnail()->original()); - const auto documentId = ready.id; + const auto documentId = ready->id; _uploadId = FullMsgId( session->userPeerId(), session->data().nextLocalMessageId()); - session->uploader().uploadMedia(_uploadId, ready); + session->uploader().upload(_uploadId, ready); if (_uploadLifetime) { return; } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 43966331e..b4d8bb49c 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/fields/input_field.h" #include "mtproto/sender.h" -struct FileLoadResult; enum class SendMediaType; class MessageLinksParser; struct InlineBotQuery; diff --git a/Telegram/SourceFiles/iv/iv_data.cpp b/Telegram/SourceFiles/iv/iv_data.cpp index bbcca0b2c..5258e6c9f 100644 --- a/Telegram/SourceFiles/iv/iv_data.cpp +++ b/Telegram/SourceFiles/iv/iv_data.cpp @@ -25,7 +25,7 @@ QByteArray GeoPointId(Geo point) { const auto lon = int(point.lon * 1000000); const auto combined = (std::uint64_t(std::uint32_t(lat)) << 32) | std::uint64_t(std::uint32_t(lon)); - return QByteArray::number(combined) + return QByteArray::number(quint64(combined)) + ',' + QByteArray::number(point.access); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 10bea63d4..809086765 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -16,7 +16,6 @@ struct HistoryMessageMarkupButton; class MainWindow; class HistoryWidget; class StackItem; -struct FileLoadResult; class History; class Image; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 684163163..980888ffb 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -1581,14 +1581,10 @@ void FormController::uploadEncryptedFile( &session(), std::make_unique(std::move(data))); - auto prepared = std::make_shared( - TaskId(), - file.uploadData->fileId, - FileLoadTo(PeerId(), Api::SendOptions(), FullReplyTo(), MsgId()), - TextWithTags(), - false, - std::shared_ptr(nullptr)); - prepared->type = SendMediaType::Secure; + auto prepared = MakePreparedFile({ + .id = file.uploadData->fileId, + .type = SendMediaType::Secure, + }); prepared->content = QByteArray::fromRawData( reinterpret_cast(file.uploadData->bytes.data()), file.uploadData->bytes.size()); diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index c40396955..e0c7f8b57 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -59,22 +59,15 @@ constexpr auto kKillSessionTimeout = 15 * crl::time(1000); } // namespace struct Uploader::File { - File(const SendMediaReady &media); - File(const std::shared_ptr &file); + explicit File(const std::shared_ptr &file); void setDocSize(int64 size); bool setPartSize(uint32 partSize); - std::shared_ptr file; - SendMediaReady media; + std::shared_ptr file; int32 partsCount = 0; mutable int64 fileSentSize = 0; - uint64 id() const; - SendMediaType type() const; - uint64 thumbId() const; - const QString &filename() const; - HashMd5 md5Hash; std::unique_ptr docFile; @@ -85,27 +78,15 @@ struct Uploader::File { }; -Uploader::File::File(const SendMediaReady &media) : media(media) { - partsCount = media.parts.size(); - if (type() == SendMediaType::File - || type() == SendMediaType::ThemeFile - || type() == SendMediaType::Audio) { - setDocSize(media.file.isEmpty() - ? media.data.size() - : media.filesize); - } else { - docSize = docPartSize = docPartsCount = 0; - } -} -Uploader::File::File(const std::shared_ptr &file) +Uploader::File::File(const std::shared_ptr &file) : file(file) { - partsCount = (type() == SendMediaType::Photo - || type() == SendMediaType::Secure) + partsCount = (file->type == SendMediaType::Photo + || file->type == SendMediaType::Secure) ? file->fileparts.size() : file->thumbparts.size(); - if (type() == SendMediaType::File - || type() == SendMediaType::ThemeFile - || type() == SendMediaType::Audio) { + if (file->type == SendMediaType::File + || file->type == SendMediaType::ThemeFile + || file->type == SendMediaType::Audio) { setDocSize(file->filesize); } else { docSize = docPartSize = docPartsCount = 0; @@ -134,22 +115,6 @@ bool Uploader::File::setPartSize(uint32 partSize) { return (docPartsCount <= kDocumentMaxPartsCountDefault); } -uint64 Uploader::File::id() const { - return file ? file->id : media.id; -} - -SendMediaType Uploader::File::type() const { - return file ? file->type : media.type; -} - -uint64 Uploader::File::thumbId() const { - return file ? file->thumbId : media.thumbId; -} - -const QString &Uploader::File::filename() const { - return file ? file->filename : media.filename; -} - Uploader::Uploader(not_null api) : _api(api) , _nextTimer([=] { sendNext(); }) @@ -282,39 +247,9 @@ Main::Session &Uploader::session() const { return _api->session(); } -void Uploader::uploadMedia( - const FullMsgId &msgId, - const SendMediaReady &media) { - if (media.type == SendMediaType::Photo) { - session().data().processPhoto(media.photo, media.photoThumbs); - } else if (media.type == SendMediaType::File - || media.type == SendMediaType::ThemeFile - || media.type == SendMediaType::Audio) { - const auto document = media.photoThumbs.empty() - ? session().data().processDocument(media.document) - : session().data().processDocument( - media.document, - Images::FromImageInMemory( - media.photoThumbs.front().second.image, - "JPG", - media.photoThumbs.front().second.bytes)); - if (!media.data.isEmpty()) { - document->setDataAndCache(media.data); - if (media.type == SendMediaType::ThemeFile) { - document->checkWallPaperProperties(); - } - } - if (!media.file.isEmpty()) { - document->setLocation(Core::FileLocation(media.file)); - } - } - queue.emplace(msgId, File(media)); - sendNext(); -} - void Uploader::upload( const FullMsgId &msgId, - const std::shared_ptr &file) { + const std::shared_ptr &file) { if (file->type == SendMediaType::Photo) { const auto photo = session().data().processPhoto( file->photo, @@ -383,13 +318,13 @@ void Uploader::currentFailed() { } void Uploader::notifyFailed(FullMsgId id, const File &file) { - const auto type = file.type(); + const auto type = file.file->type; if (type == SendMediaType::Photo) { _photoFailed.fire_copy(id); } else if (type == SendMediaType::File || type == SendMediaType::ThemeFile || type == SendMediaType::Audio) { - const auto document = session().data().document(file.id()); + const auto document = session().data().document(file.file->id); if (document->uploading()) { document->status = FileUploadFailed; } @@ -439,18 +374,14 @@ void Uploader::sendNext() { } } - auto &parts = uploadingData.file - ? ((uploadingData.type() == SendMediaType::Photo - || uploadingData.type() == SendMediaType::Secure) - ? uploadingData.file->fileparts - : uploadingData.file->thumbparts) - : uploadingData.media.parts; - const auto partsOfId = uploadingData.file - ? ((uploadingData.type() == SendMediaType::Photo - || uploadingData.type() == SendMediaType::Secure) - ? uploadingData.file->id - : uploadingData.file->thumbId) - : uploadingData.media.thumbId; + auto &parts = (uploadingData.file->type == SendMediaType::Photo + || uploadingData.file->type == SendMediaType::Secure) + ? uploadingData.file->fileparts + : uploadingData.file->thumbparts; + const auto partsOfId = (uploadingData.file->type == SendMediaType::Photo + || uploadingData.file->type == SendMediaType::Secure) + ? uploadingData.file->id + : uploadingData.file->thumbId; if (parts.isEmpty()) { if (uploadingData.docSentParts >= uploadingData.docPartsCount) { if (requestsSent.empty() && docRequestsSent.empty()) { @@ -462,19 +393,17 @@ void Uploader::sendNext() { const auto attachedStickers = uploadingData.file ? uploadingData.file->attachedStickers : std::vector(); - if (uploadingData.type() == SendMediaType::Photo) { - auto photoFilename = uploadingData.filename(); + if (uploadingData.file->type == SendMediaType::Photo) { + auto photoFilename = uploadingData.file->filename; if (!photoFilename.endsWith(u".jpg"_q, Qt::CaseInsensitive)) { // Server has some extensions checking for inputMediaUploadedPhoto, // so force the extension to be .jpg anyway. It doesn't matter, // because the filename from inputFile is not used anywhere. photoFilename += u".jpg"_q; } - const auto md5 = uploadingData.file - ? uploadingData.file->filemd5 - : uploadingData.media.jpeg_md5; + const auto md5 = uploadingData.file->filemd5; const auto file = MTP_inputFile( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.partsCount), MTP_string(photoFilename), MTP_bytes(md5)); @@ -487,34 +416,30 @@ void Uploader::sendNext() { .options = options, .edit = edit, }); - } else if (uploadingData.type() == SendMediaType::File - || uploadingData.type() == SendMediaType::ThemeFile - || uploadingData.type() == SendMediaType::Audio) { + } else if (uploadingData.file->type == SendMediaType::File + || uploadingData.file->type == SendMediaType::ThemeFile + || uploadingData.file->type == SendMediaType::Audio) { QByteArray docMd5(32, Qt::Uninitialized); hashMd5Hex(uploadingData.md5Hash.result(), docMd5.data()); const auto file = (uploadingData.docSize > kUseBigFilesFrom) ? MTP_inputFileBig( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.filename())) + MTP_string(uploadingData.file->filename)) : MTP_inputFile( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.filename()), + MTP_string(uploadingData.file->filename), MTP_bytes(docMd5)); const auto thumb = [&]() -> std::optional { if (!uploadingData.partsCount) { return std::nullopt; } - const auto thumbFilename = uploadingData.file - ? uploadingData.file->thumbname - : (u"thumb."_q + uploadingData.media.thumbExt); - const auto thumbMd5 = uploadingData.file - ? uploadingData.file->thumbmd5 - : uploadingData.media.jpeg_md5; + const auto thumbFilename = uploadingData.file->thumbname; + const auto thumbMd5 = uploadingData.file->thumbmd5; return MTP_inputFile( - MTP_long(uploadingData.thumbId()), + MTP_long(uploadingData.file->thumbId), MTP_int(uploadingData.partsCount), MTP_string(thumbFilename), MTP_bytes(thumbMd5)); @@ -529,10 +454,10 @@ void Uploader::sendNext() { .options = options, .edit = edit, }); - } else if (uploadingData.type() == SendMediaType::Secure) { + } else if (uploadingData.file->type == SendMediaType::Secure) { _secureReady.fire({ uploadingId, - uploadingData.id(), + uploadingData.file->id, uploadingData.partsCount }); } queue.erase(uploadingId); @@ -542,15 +467,11 @@ void Uploader::sendNext() { return; } - auto &content = uploadingData.file - ? uploadingData.file->content - : uploadingData.media.data; + auto &content = uploadingData.file->content; QByteArray toSend; if (content.isEmpty()) { if (!uploadingData.docFile) { - const auto filepath = uploadingData.file - ? uploadingData.file->filepath - : uploadingData.media.file; + const auto filepath = uploadingData.file->filepath; uploadingData.docFile = std::make_unique(filepath); if (!uploadingData.docFile->open(QIODevice::ReadOnly)) { currentFailed(); @@ -565,9 +486,9 @@ void Uploader::sendNext() { const auto offset = uploadingData.docSentParts * uploadingData.docPartSize; toSend = content.mid(offset, uploadingData.docPartSize); - if ((uploadingData.type() == SendMediaType::File - || uploadingData.type() == SendMediaType::ThemeFile - || uploadingData.type() == SendMediaType::Audio) + if ((uploadingData.file->type == SendMediaType::File + || uploadingData.file->type == SendMediaType::ThemeFile + || uploadingData.file->type == SendMediaType::Audio) && uploadingData.docSentParts <= kUseBigFilesFrom) { uploadingData.md5Hash.feed(toSend.constData(), toSend.size()); } @@ -581,7 +502,7 @@ void Uploader::sendNext() { mtpRequestId requestId; if (uploadingData.docSize > kUseBigFilesFrom) { requestId = _api->request(MTPupload_SaveBigFilePart( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docSentParts), MTP_int(uploadingData.docPartsCount), MTP_bytes(toSend) @@ -592,7 +513,7 @@ void Uploader::sendNext() { }).toDC(MTP::uploadDcId(todc)).send(); } else { requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docSentParts), MTP_bytes(toSend) )).done([=](const MTPBool &result, mtpRequestId requestId) { @@ -723,18 +644,18 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } sentSize -= sentPartSize; sentSizes[dc] -= sentPartSize; - if (file.type() == SendMediaType::Photo) { + if (file.file->type == SendMediaType::Photo) { file.fileSentSize += sentPartSize; - const auto photo = session().data().photo(file.id()); + const auto photo = session().data().photo(file.file->id); if (photo->uploading() && file.file) { photo->uploadingData->size = file.file->partssize; photo->uploadingData->offset = file.fileSentSize; } _photoProgress.fire_copy(fullId); - } else if (file.type() == SendMediaType::File - || file.type() == SendMediaType::ThemeFile - || file.type() == SendMediaType::Audio) { - const auto document = session().data().document(file.id()); + } else if (file.file->type == SendMediaType::File + || file.file->type == SendMediaType::ThemeFile + || file.file->type == SendMediaType::Audio) { + const auto document = session().data().document(file.file->id); if (document->uploading()) { const auto doneParts = file.docSentParts - int(docRequestsSent.size()); @@ -743,7 +664,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { doneParts * file.docPartSize); } _documentProgress.fire_copy(fullId); - } else if (file.type() == SendMediaType::Secure) { + } else if (file.file->type == SendMediaType::Secure) { file.fileSentSize += sentPartSize; _secureProgress.fire_copy({ fullId, diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 2e68307b9..4347e5c03 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -12,8 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/facade.h" class ApiWrap; -struct FileLoadResult; -struct SendMediaReady; +struct FilePrepareResult; namespace Api { enum class SendProgressType; @@ -58,10 +57,9 @@ public: return uploadingId; } - void uploadMedia(const FullMsgId &msgId, const SendMediaReady &image); void upload( const FullMsgId &msgId, - const std::shared_ptr &file); + const std::shared_ptr &file); void cancel(const FullMsgId &msgId); void pause(const FullMsgId &msgId); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index c26b68458..192c7c112 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -222,42 +222,6 @@ int PhotoSideLimit() { return PhotoSideLimit(SendLargePhotos.value()); } -SendMediaReady::SendMediaReady( - SendMediaType type, - const QString &file, - const QString &filename, - int64 filesize, - const QByteArray &data, - const uint64 &id, - const uint64 &thumbId, - const QString &thumbExt, - const PeerId &peer, - const MTPPhoto &photo, - const PreparedPhotoThumbs &photoThumbs, - const MTPDocument &document, - const QByteArray &jpeg) -: type(type) -, file(file) -, filename(filename) -, filesize(filesize) -, data(data) -, thumbExt(thumbExt) -, id(id) -, thumbId(thumbId) -, peer(peer) -, photo(photo) -, document(document) -, photoThumbs(photoThumbs) { - if (!jpeg.isEmpty()) { - int32 size = jpeg.size(); - for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) { - parts.insert(part, jpeg.mid(i, kPhotoUploadPartSize)); - } - jpeg_md5.resize(32); - hashMd5Hex(jpeg.constData(), jpeg.size(), jpeg_md5.data()); - } -} - TaskQueue::TaskQueue(crl::time stopTimeoutMs) { if (stopTimeoutMs > 0) { _stopTimer = new QTimer(this); @@ -455,22 +419,17 @@ SendingAlbum::Item::Item(TaskId taskId) : taskId(taskId) { } -FileLoadResult::FileLoadResult( - TaskId taskId, - uint64 id, - const FileLoadTo &to, - const TextWithTags &caption, - bool spoiler, - std::shared_ptr album) -: taskId(taskId) -, id(id) -, to(to) -, album(std::move(album)) -, caption(caption) -, spoiler(spoiler) { +FilePrepareResult::FilePrepareResult(FilePrepareDescriptor &&descriptor) +: taskId(descriptor.taskId) +, id(descriptor.id) +, to(std::move(descriptor.to)) +, album(std::move(descriptor.album)) +, type(descriptor.type) +, caption(std::move(descriptor.caption)) +, spoiler(descriptor.spoiler) { } -void FileLoadResult::setFileData(const QByteArray &filedata) { +void FilePrepareResult::setFileData(const QByteArray &filedata) { if (filedata.isEmpty()) { partssize = 0; } else { @@ -483,7 +442,7 @@ void FileLoadResult::setFileData(const QByteArray &filedata) { } } -void FileLoadResult::setThumbData(const QByteArray &thumbdata) { +void FilePrepareResult::setThumbData(const QByteArray &thumbdata) { if (!thumbdata.isEmpty()) { thumbbytes = thumbdata; int32 size = thumbdata.size(); @@ -495,6 +454,11 @@ void FileLoadResult::setThumbData(const QByteArray &thumbdata) { } } +std::shared_ptr MakePreparedFile( + FilePrepareDescriptor &&descriptor) { + return std::make_shared(std::move(descriptor)); +} + FileLoadTask::FileLoadTask( not_null session, const QString &filepath, @@ -710,13 +674,14 @@ bool FileLoadTask::FillImageInformation( } void FileLoadTask::process(Args &&args) { - _result = std::make_shared( - id(), - _id, - _to, - _caption, - _spoiler, - _album); + _result = MakePreparedFile({ + .taskId = id(), + .id = _id, + .to = _to, + .caption = _caption, + .spoiler = _spoiler, + .album = _album, + }); QString filename, filemime; qint64 filesize = 0; @@ -1062,7 +1027,7 @@ void FileLoadTask::finish() { } } -FileLoadResult *FileLoadTask::peekResult() const { +FilePrepareResult *FileLoadTask::peekResult() const { return _result.get(); } diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index d2e64b536..8b3b7bca7 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -36,43 +36,8 @@ enum class SendMediaType { Secure, }; -using UploadFileParts = QMap; -struct SendMediaReady { - SendMediaReady() = default; // temp - SendMediaReady( - SendMediaType type, - const QString &file, - const QString &filename, - int64 filesize, - const QByteArray &data, - const uint64 &id, - const uint64 &thumbId, - const QString &thumbExt, - const PeerId &peer, - const MTPPhoto &photo, - const PreparedPhotoThumbs &photoThumbs, - const MTPDocument &document, - const QByteArray &jpeg); - - SendMediaType type; - QString file, filename; - int64 filesize = 0; - QByteArray data; - QString thumbExt; - uint64 id, thumbId; // id always file-id of media, thumbId is file-id of thumb ( == id for photos) - PeerId peer; - - MTPPhoto photo; - MTPDocument document; - PreparedPhotoThumbs photoThumbs; - UploadFileParts parts; - QByteArray jpeg_md5; - - QString caption; - -}; - using TaskId = void*; // no interface, just id +inline constexpr auto kEmptyTaskId = TaskId(); class Task { public: @@ -144,7 +109,7 @@ struct SendingAlbum { struct Item { explicit Item(TaskId taskId); - TaskId taskId; + TaskId taskId = kEmptyTaskId; uint64 randomId = 0; FullMsgId msgId; std::optional media; @@ -182,17 +147,21 @@ struct FileLoadTo { MsgId replaceMediaOf; }; -struct FileLoadResult { - FileLoadResult( - TaskId taskId, - uint64 id, - const FileLoadTo &to, - const TextWithTags &caption, - bool spoiler, - std::shared_ptr album); +using UploadFileParts = QMap; +struct FilePrepareDescriptor { + TaskId taskId = kEmptyTaskId; + base::required id; + SendMediaType type = SendMediaType::File; + FileLoadTo to = { PeerId(), Api::SendOptions(), FullReplyTo(), MsgId() }; + TextWithTags caption; + bool spoiler = false; + std::shared_ptr album; +}; +struct FilePrepareResult { + explicit FilePrepareResult(FilePrepareDescriptor &&descriptor); - TaskId taskId; - uint64 id; + TaskId taskId = kEmptyTaskId; + uint64 id = 0; FileLoadTo to; std::shared_ptr album; SendMediaType type = SendMediaType::File; @@ -216,8 +185,8 @@ struct FileLoadResult { QImage goodThumbnail; QByteArray goodThumbnailBytes; - MTPPhoto photo; - MTPDocument document; + MTPPhoto photo = MTP_photoEmpty(MTP_long(0)); + MTPDocument document = MTP_documentEmpty(MTP_long(0)); PreparedPhotoThumbs photoThumbs; TextWithTags caption; @@ -230,6 +199,9 @@ struct FileLoadResult { }; +[[nodiscard]] std::shared_ptr MakePreparedFile( + FilePrepareDescriptor &&descriptor); + class FileLoadTask final : public Task { public: static std::unique_ptr ReadMediaInformation( @@ -276,7 +248,7 @@ public: } void finish() override; - FileLoadResult *peekResult() const; + FilePrepareResult *peekResult() const; private: static bool CheckForSong( @@ -312,6 +284,6 @@ private: TextWithTags _caption; bool _spoiler = false; - std::shared_ptr _result; + std::shared_ptr _result; }; diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 7409a95ee..b225b321e 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -585,11 +585,11 @@ void ChatBackground::checkUploadWallPaper() { } const auto ready = PrepareWallPaper(_session->mainDcId(), _original); - const auto documentId = ready.id; + const auto documentId = ready->id; _wallPaperUploadId = FullMsgId( _session->userPeerId(), _session->data().nextLocalMessageId()); - _session->uploader().uploadMedia(_wallPaperUploadId, ready); + _session->uploader().upload(_wallPaperUploadId, ready); if (_wallPaperUploadLifetime) { return; } @@ -1529,7 +1529,9 @@ bool ReadPaletteValues(const QByteArray &content, Fn PrepareWallPaper( + MTP::DcId dcId, + const QImage &image) { PreparedPhotoThumbs thumbnails; QVector sizes; @@ -1555,6 +1557,7 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { }; push("s", scaled(320)); + const auto id = base::RandomValue(); const auto filename = u"wallpaper.jpg"_q; auto attributes = QVector( 1, @@ -1562,8 +1565,20 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { attributes.push_back(MTP_documentAttributeImageSize( MTP_int(image.width()), MTP_int(image.height()))); - const auto id = base::RandomValue(); - const auto document = MTP_document( + + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::ThemeFile, + }); + result->filename = filename; + result->content = jpeg; + result->filesize = jpeg.size(); + result->setFileData(jpeg); + if (thumbnails.empty()) { + result->thumb = thumbnails.front().second.image; + result->thumbbytes = thumbnails.front().second.bytes; + } + result->document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -1575,21 +1590,7 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { MTPVector(), MTP_int(dcId), MTP_vector(attributes)); - - return SendMediaReady( - SendMediaType::ThemeFile, - QString(), // filepath - filename, - jpeg.size(), - jpeg, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - thumbnails, - document, - QByteArray()); + return result; } std::unique_ptr DefaultChatThemeOn(rpl::lifetime &lifetime) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 1425e4d07..b0497fb0c 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/style/style_core_palette.h" class QFileSystemWatcher; -struct SendMediaReady; +struct FilePrepareResult; namespace style { struct colorizer; @@ -298,7 +298,7 @@ private: }; -[[nodiscard]] SendMediaReady PrepareWallPaper( +[[nodiscard]] std::shared_ptr PrepareWallPaper( MTP::DcId dcId, const QImage &image); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index d448ca185..c430b3e28 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -385,7 +385,7 @@ bool CopyColorsToPalette( }); } -SendMediaReady PrepareThemeMedia( +std::shared_ptr PrepareThemeMedia( MTP::DcId dcId, const QString &name, const QByteArray &content) { @@ -403,28 +403,29 @@ SendMediaReady PrepareThemeMedia( thumbnail.save(&buffer, "JPG", 87); } - const auto push = [&]( - const char *type, - QImage &&image, - QByteArray bytes = QByteArray()) { - sizes.push_back(MTP_photoSize( - MTP_string(type), - MTP_int(image.width()), - MTP_int(image.height()), MTP_int(0))); - thumbnails.emplace(type[0], PreparedPhotoThumb{ - .image = std::move(image), - .bytes = std::move(bytes) - }); - }; - push("s", std::move(thumbnail), thumbnailBytes); + sizes.push_back(MTP_photoSize( + MTP_string("s"), + MTP_int(thumbnail.width()), + MTP_int(thumbnail.height()), MTP_int(0))); + const auto id = base::RandomValue(); const auto filename = base::FileNameFromUserString(name) + u".tdesktop-theme"_q; auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - const auto id = base::RandomValue(); - const auto document = MTP_document( + + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::ThemeFile, + }); + result->filename = filename; + result->content = content; + result->filesize = content.size(); + result->thumb = thumbnail; + result->thumbname = "thumb.jpg"; + result->setThumbData(thumbnailBytes); + result->document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -436,21 +437,7 @@ SendMediaReady PrepareThemeMedia( MTPVector(), MTP_int(dcId), MTP_vector(attributes)); - - return SendMediaReady( - SendMediaType::ThemeFile, - QString(), // filepath - filename, - content.size(), - content, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - thumbnails, - document, - thumbnailBytes); + return result; } Fn SavePreparedTheme( @@ -570,7 +557,7 @@ Fn SavePreparedTheme( session->mainDcId(), fields.title, theme); - state->filename = media.filename; + state->filename = media->filename; state->themeContent = theme; session->uploader().documentReady( @@ -580,7 +567,7 @@ Fn SavePreparedTheme( uploadTheme(data); }, state->lifetime); - session->uploader().uploadMedia(state->id, media); + session->uploader().upload(state->id, media); }; const auto save = [=] { @@ -999,12 +986,15 @@ ParsedTheme ParseTheme( [[nodiscard]] QString GenerateSlug() { const auto letters = uint8('Z' + 1 - 'A'); const auto digits = uint8('9' + 1 - '0'); + const auto firstValues = uint8(2 * letters); const auto values = uint8(2 * letters + digits); auto result = QString(); result.reserve(kRandomSlugSize); for (auto i = 0; i != kRandomSlugSize; ++i) { - const auto value = base::RandomValue() % values; + const auto value = i + ? (base::RandomValue() % values) + : (base::RandomValue() % firstValues); if (value < letters) { result.append(char('A' + value)); } else if (value < 2 * letters) { From aaaeea297927211fe4e5ea09ce76b324eae24920 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 3 Apr 2024 12:39:08 +0400 Subject: [PATCH 18/71] Use plain vector for prepared upload parts. --- Telegram/SourceFiles/storage/file_upload.cpp | 160 ++++++++---------- Telegram/SourceFiles/storage/file_upload.h | 14 +- .../SourceFiles/storage/localimageloader.cpp | 8 +- .../SourceFiles/storage/localimageloader.h | 2 +- 4 files changed, 88 insertions(+), 96 deletions(-) diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index e0c7f8b57..b19af82df 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -65,7 +65,10 @@ struct Uploader::File { bool setPartSize(uint32 partSize); std::shared_ptr file; - int32 partsCount = 0; + const std::vector &parts; + const uint64 partsOfId = 0; + int partsSent = 0; + mutable int64 fileSentSize = 0; HashMd5 md5Hash; @@ -79,11 +82,15 @@ struct Uploader::File { }; Uploader::File::File(const std::shared_ptr &file) -: file(file) { - partsCount = (file->type == SendMediaType::Photo - || file->type == SendMediaType::Secure) - ? file->fileparts.size() - : file->thumbparts.size(); +: file(file) +, parts((file->type == SendMediaType::Photo + || file->type == SendMediaType::Secure) + ? file->fileparts + : file->thumbparts) +, partsOfId((file->type == SendMediaType::Photo + || file->type == SendMediaType::Secure) + ? file->id + : file->thumbId) { if (file->type == SendMediaType::File || file->type == SendMediaType::ThemeFile || file->type == SendMediaType::Audio) { @@ -174,7 +181,7 @@ Uploader::Uploader(not_null api) _api->instance().nonPremiumDelayedRequests( ) | rpl::start_with_next([=](mtpRequestId id) { - if (dcMap.contains(id)) { + if (_dcIndices.contains(id)) { _nonPremiumDelayed.emplace(id); } }, _lifetime); @@ -299,7 +306,7 @@ void Uploader::upload( } void Uploader::currentFailed() { - auto j = queue.find(uploadingId); + auto j = queue.find(_uploadingId); if (j != queue.end()) { const auto [msgId, file] = std::move(*j); queue.erase(j); @@ -307,11 +314,11 @@ void Uploader::currentFailed() { } cancelRequests(); - dcMap.clear(); - uploadingId = FullMsgId(); - sentSize = 0; + _dcIndices.clear(); + _uploadingId = FullMsgId(); + _sentTotal = 0; for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { - sentSizes[i] = 0; + _sentPerDc[i] = 0; } sendNext(); @@ -343,7 +350,7 @@ void Uploader::stopSessions() { } void Uploader::sendNext() { - if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) { + if (_sentTotal >= kMaxUploadFileParallelSize || _pausedId.msg) { return; } @@ -358,33 +365,25 @@ void Uploader::sendNext() { if (stopping) { _stopSessionsTimer.cancel(); } - auto i = uploadingId.msg ? queue.find(uploadingId) : queue.begin(); - if (!uploadingId.msg) { - uploadingId = i->first; + auto i = _uploadingId.msg ? queue.find(_uploadingId) : queue.begin(); + if (!_uploadingId.msg) { + _uploadingId = i->first; } else if (i == queue.end()) { i = queue.begin(); - uploadingId = i->first; + _uploadingId = i->first; } auto &uploadingData = i->second; auto todc = 0; for (auto dc = 1; dc != MTP::kUploadSessionsCount; ++dc) { - if (sentSizes[dc] < sentSizes[todc]) { + if (_sentPerDc[dc] < _sentPerDc[todc]) { todc = dc; } } - auto &parts = (uploadingData.file->type == SendMediaType::Photo - || uploadingData.file->type == SendMediaType::Secure) - ? uploadingData.file->fileparts - : uploadingData.file->thumbparts; - const auto partsOfId = (uploadingData.file->type == SendMediaType::Photo - || uploadingData.file->type == SendMediaType::Secure) - ? uploadingData.file->id - : uploadingData.file->thumbId; - if (parts.isEmpty()) { + if (uploadingData.partsSent >= uploadingData.parts.size()) { if (uploadingData.docSentParts >= uploadingData.docPartsCount) { - if (requestsSent.empty() && docRequestsSent.empty()) { + if (_sentSizes.empty()) { const auto options = uploadingData.file ? uploadingData.file->to.options : Api::SendOptions(); @@ -404,11 +403,11 @@ void Uploader::sendNext() { const auto md5 = uploadingData.file->filemd5; const auto file = MTP_inputFile( MTP_long(uploadingData.file->id), - MTP_int(uploadingData.partsCount), + MTP_int(uploadingData.parts.size()), MTP_string(photoFilename), MTP_bytes(md5)); _photoReady.fire({ - .fullId = uploadingId, + .fullId = _uploadingId, .info = { .file = file, .attachedStickers = attachedStickers, @@ -433,19 +432,19 @@ void Uploader::sendNext() { MTP_string(uploadingData.file->filename), MTP_bytes(docMd5)); const auto thumb = [&]() -> std::optional { - if (!uploadingData.partsCount) { + if (uploadingData.parts.empty()) { return std::nullopt; } const auto thumbFilename = uploadingData.file->thumbname; const auto thumbMd5 = uploadingData.file->thumbmd5; return MTP_inputFile( MTP_long(uploadingData.file->thumbId), - MTP_int(uploadingData.partsCount), + MTP_int(uploadingData.parts.size()), MTP_string(thumbFilename), MTP_bytes(thumbMd5)); }(); _documentReady.fire({ - .fullId = uploadingId, + .fullId = _uploadingId, .info = { .file = file, .thumb = thumb, @@ -456,12 +455,13 @@ void Uploader::sendNext() { }); } else if (uploadingData.file->type == SendMediaType::Secure) { _secureReady.fire({ - uploadingId, + _uploadingId, uploadingData.file->id, - uploadingData.partsCount }); + int(uploadingData.parts.size()), + }); } - queue.erase(uploadingId); - uploadingId = FullMsgId(); + queue.erase(_uploadingId); + _uploadingId = FullMsgId(); sendNext(); } return; @@ -522,36 +522,37 @@ void Uploader::sendNext() { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); } - docRequestsSent.emplace(requestId, uploadingData.docSentParts); - dcMap.emplace(requestId, todc); - sentSize += uploadingData.docPartSize; - sentSizes[todc] += uploadingData.docPartSize; + _sentSizes.emplace(requestId, uploadingData.docPartSize); + _docSentRequests.emplace(requestId); + _dcIndices.emplace(requestId, todc); + _sentTotal += uploadingData.docPartSize; + _sentPerDc[todc] += uploadingData.docPartSize; uploadingData.docSentParts++; } else { - auto part = parts.begin(); + const auto index = uploadingData.partsSent++; + const auto partBytes = uploadingData.parts[index]; + const auto partSize = int(partBytes.size()); const auto requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(partsOfId), - MTP_int(part.key()), - MTP_bytes(part.value()) + MTP_long(uploadingData.partsOfId), + MTP_int(index), + MTP_bytes(partBytes) )).done([=](const MTPBool &result, mtpRequestId requestId) { partLoaded(result, requestId); }).fail([=](const MTP::Error &error, mtpRequestId requestId) { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); - requestsSent.emplace(requestId, part.value()); - dcMap.emplace(requestId, todc); - sentSize += part.value().size(); - sentSizes[todc] += part.value().size(); - - parts.erase(part); + _sentSizes.emplace(requestId, partSize); + _dcIndices.emplace(requestId, todc); + _sentTotal += partSize; + _sentPerDc[todc] += partSize; } _nextTimer.callOnce(kUploadRequestInterval); } void Uploader::cancel(const FullMsgId &msgId) { - if (uploadingId == msgId) { + if (_uploadingId == msgId) { currentFailed(); } else { queue.erase(msgId); @@ -559,12 +560,12 @@ void Uploader::cancel(const FullMsgId &msgId) { } void Uploader::cancelAll() { - const auto single = queue.empty() ? uploadingId : queue.begin()->first; + const auto single = queue.empty() ? _uploadingId : queue.begin()->first; if (!single) { return; } _pausedId = single; - if (uploadingId) { + if (_uploadingId) { currentFailed(); } while (!queue.empty()) { @@ -589,61 +590,49 @@ void Uploader::confirm(const FullMsgId &msgId) { } void Uploader::cancelRequests() { - for (const auto &requestData : requestsSent) { + _docSentRequests.clear(); + for (const auto &requestData : _sentSizes) { _api->request(requestData.first).cancel(); } - requestsSent.clear(); - for (const auto &requestData : docRequestsSent) { - _api->request(requestData.first).cancel(); - } - docRequestsSent.clear(); + _sentSizes.clear(); } void Uploader::clear() { queue.clear(); cancelRequests(); - dcMap.clear(); - sentSize = 0; + _dcIndices.clear(); + _sentTotal = 0; for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { _api->instance().stopSession(MTP::uploadDcId(i)); - sentSizes[i] = 0; + _sentPerDc[i] = 0; } _stopSessionsTimer.cancel(); } void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { - auto j = docRequestsSent.end(); - auto i = requestsSent.find(requestId); - if (i == requestsSent.cend()) { - j = docRequestsSent.find(requestId); - } + _docSentRequests.remove(requestId); + auto i = _sentSizes.find(requestId); const auto wasNonPremiumDelayed = _nonPremiumDelayed.remove(requestId); - if (i != requestsSent.cend() || j != docRequestsSent.cend()) { + if (i != _sentSizes.cend()) { if (mtpIsFalse(result)) { // failed to upload current file currentFailed(); return; } else { - auto dcIt = dcMap.find(requestId); - if (dcIt == dcMap.cend()) { // must not happen + auto dcIt = _dcIndices.find(requestId); + if (dcIt == _dcIndices.cend()) { // must not happen currentFailed(); return; } auto dc = dcIt->second; - dcMap.erase(dcIt); + _dcIndices.erase(dcIt); - int64 sentPartSize = 0; - auto k = queue.find(uploadingId); + int64 sentPartSize = i->second; + auto k = queue.find(_uploadingId); Assert(k != queue.cend()); auto &[fullId, file] = *k; - if (i != requestsSent.cend()) { - sentPartSize = i->second.size(); - requestsSent.erase(i); - } else { - sentPartSize = file.docPartSize; - docRequestsSent.erase(j); - } - sentSize -= sentPartSize; - sentSizes[dc] -= sentPartSize; + _sentSizes.erase(i); + _sentTotal -= sentPartSize; + _sentPerDc[dc] -= sentPartSize; if (file.file->type == SendMediaType::Photo) { file.fileSentSize += sentPartSize; const auto photo = session().data().photo(file.file->id); @@ -658,7 +647,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { const auto document = session().data().document(file.file->id); if (document->uploading()) { const auto doneParts = file.docSentParts - - int(docRequestsSent.size()); + - int(_docSentRequests.size()); document->uploadingData->offset = std::min( document->uploadingData->size, doneParts * file.docPartSize); @@ -683,8 +672,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { void Uploader::partFailed(const MTP::Error &error, mtpRequestId requestId) { // failed to upload current file _nonPremiumDelayed.remove(requestId); - if ((requestsSent.find(requestId) != requestsSent.cend()) - || (docRequestsSent.find(requestId) != docRequestsSent.cend())) { + if (_sentSizes.find(requestId) != _sentSizes.cend()) { currentFailed(); } sendNext(); diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 4347e5c03..a733775ad 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -54,7 +54,7 @@ public: [[nodiscard]] Main::Session &session() const; [[nodiscard]] FullMsgId currentUploadId() const { - return uploadingId; + return _uploadingId; } void upload( @@ -125,14 +125,14 @@ private: int progress = 0); const not_null _api; - base::flat_map requestsSent; - base::flat_map docRequestsSent; - base::flat_map dcMap; + base::flat_map _sentSizes; + base::flat_set _docSentRequests; + base::flat_map _dcIndices; base::flat_set _nonPremiumDelayed; - uint32 sentSize = 0; // FileSize: Right now any file size fits 32 bit. - uint32 sentSizes[MTP::kUploadSessionsCount] = { 0 }; + uint32 _sentTotal = 0; // FileSize: Right now any file size fits 32 bit. + uint32 _sentPerDc[MTP::kUploadSessionsCount] = { 0 }; - FullMsgId uploadingId; + FullMsgId _uploadingId; FullMsgId _pausedId; std::map queue; base::Timer _nextTimer, _stopSessionsTimer; diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 192c7c112..59f2fb505 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -434,8 +434,10 @@ void FilePrepareResult::setFileData(const QByteArray &filedata) { partssize = 0; } else { partssize = filedata.size(); + fileparts.reserve( + (partssize + kPhotoUploadPartSize - 1) / kPhotoUploadPartSize); for (int32 i = 0, part = 0; i < partssize; i += kPhotoUploadPartSize, ++part) { - fileparts.insert(part, filedata.mid(i, kPhotoUploadPartSize)); + fileparts.push_back(filedata.mid(i, kPhotoUploadPartSize)); } filemd5.resize(32); hashMd5Hex(filedata.constData(), filedata.size(), filemd5.data()); @@ -446,8 +448,10 @@ void FilePrepareResult::setThumbData(const QByteArray &thumbdata) { if (!thumbdata.isEmpty()) { thumbbytes = thumbdata; int32 size = thumbdata.size(); + thumbparts.reserve( + (size + kPhotoUploadPartSize - 1) / kPhotoUploadPartSize); for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) { - thumbparts.insert(part, thumbdata.mid(i, kPhotoUploadPartSize)); + thumbparts.push_back(thumbdata.mid(i, kPhotoUploadPartSize)); } thumbmd5.resize(32); hashMd5Hex(thumbdata.constData(), thumbdata.size(), thumbmd5.data()); diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 8b3b7bca7..2756ac244 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -147,7 +147,7 @@ struct FileLoadTo { MsgId replaceMediaOf; }; -using UploadFileParts = QMap; +using UploadFileParts = std::vector; struct FilePrepareDescriptor { TaskId taskId = kEmptyTaskId; base::required id; From e2a0483b9ffec256faad962fc42f75b13d0c0b53 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 3 Apr 2024 19:09:00 +0400 Subject: [PATCH 19/71] Enable artifacts for GH actions --- .github/workflows/linux.yml | 2 +- .github/workflows/mac.yml | 2 +- .github/workflows/mac_packaged.yml | 2 +- .github/workflows/snap.yml | 2 +- .github/workflows/win.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fe6d3752c..e7a19397f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -52,7 +52,7 @@ jobs: - "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION" env: - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" steps: - name: Get repository name. diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 61561be9f..8271664c0 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -47,7 +47,7 @@ jobs: defines: - "" env: - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" ONLY_CACHE: "false" PREPARE_PATH: "Telegram/build/prepare/prepare.py" diff --git a/.github/workflows/mac_packaged.yml b/.github/workflows/mac_packaged.yml index 51ba9917a..8ed4ffca1 100644 --- a/.github/workflows/mac_packaged.yml +++ b/.github/workflows/mac_packaged.yml @@ -50,7 +50,7 @@ jobs: env: GIT: "https://github.com" OPENALDIR: "/usr/local/opt/openal-soft" - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" ONLY_CACHE: "false" MANUAL_CACHING: "1" AUTO_CACHING: "1" diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml index c2e5dbafe..0c2bc4148 100644 --- a/.github/workflows/snap.yml +++ b/.github/workflows/snap.yml @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-20.04 env: - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" steps: - name: Clone. diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index b42906c7c..f8332c36f 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -50,7 +50,7 @@ jobs: generator: ["", "Ninja Multi-Config"] env: - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" ONLY_CACHE: "false" PREPARE_PATH: "Telegram/build/prepare/prepare.py" From b09cede0081aabf9b014d5adbb6ab28ce34bc5e9 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Wed, 3 Apr 2024 22:04:18 +0400 Subject: [PATCH 20/71] Fix uploading artifact on Linux --- .github/workflows/linux.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e7a19397f..4eaf2ec22 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -115,8 +115,8 @@ jobs: if: env.UPLOAD_ARTIFACT == 'true' run: | cd $REPO_NAME/out/Debug - mkdir artifact - mv {Telegram,Updater} artifact/ + sudo mkdir artifact + sudo mv {Telegram,Updater} artifact/ - uses: actions/upload-artifact@master if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. From 776e80b4d6a24375ba5d3e8086458d62549f1557 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Wed, 3 Apr 2024 00:15:45 +0300 Subject: [PATCH 21/71] Fix tdesktop 4.15.6 build on OpenBSD 7.5-current with clang 16.0.6 and Qt6 fails, not entirely sure why... --- .../SourceFiles/platform/linux/notifications_manager_linux.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 1242c751f..0ea48a7de 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -37,6 +37,7 @@ namespace Notifications { namespace { using namespace gi::repository; +namespace GObject = gi::repository::GObject; constexpr auto kService = "org.freedesktop.Notifications"; constexpr auto kObjectPath = "/org/freedesktop/Notifications"; From 53b75c8ea4f98a79285040ccf21f599166e38141 Mon Sep 17 00:00:00 2001 From: Klemens Nanni Date: Wed, 3 Apr 2024 20:44:29 +0300 Subject: [PATCH 22/71] update lib_base to include OpenBSD fix --- Telegram/lib_base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/lib_base b/Telegram/lib_base index f95154033..8d4b2efa0 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit f95154033c670cf786fce82b005abde8a5d3922f +Subproject commit 8d4b2efa0024016c6a6f6b4d8f1dfc3d62bafddc From 154753c73205309b38b57af7ded618e83616f3af Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Apr 2024 17:35:51 +0300 Subject: [PATCH 23/71] Removed migrated groups from list of user's own groups. --- Telegram/SourceFiles/window/window_main_menu.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index d5c2d3fce..c49afd1a9 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -117,7 +117,9 @@ not_null AddMyChannelsBox( button->setAcceptBoth(true); const auto myChannelsBox = [=](not_null box) { - box->setTitle(tr::lng_notification_channels()); + box->setTitle(chats + ? tr::lng_notification_groups() + : tr::lng_notification_channels()); const auto st = box->lifetime().make_state( st::defaultUserpicButton); @@ -191,10 +193,16 @@ not_null AddMyChannelsBox( const auto &data = controller->session().data(); if (chats) { + auto ids = std::vector(); data.enumerateGroups([&](not_null peer) { + peer = peer->migrateToOrMe(); const auto c = peer->asChannel(); const auto g = peer->asChat(); + if (ranges::contains(ids, peer->id)) { + return; + } if ((c && c->amCreator()) || (g && g->amCreator())) { + ids.push_back(peer->id); add(peer); } }); From e32de38d040be8bb01513f8a8ca0483b22b58c71 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Apr 2024 17:43:41 +0300 Subject: [PATCH 24/71] Added url to learn more about monetization to langpack. --- Telegram/Resources/langs/lang.strings | 1 + .../info/channel_statistics/earn/info_earn_inner_widget.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 1dbe16fbb..fabd9238f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -5050,6 +5050,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_channel_earn_learn_coin_title" = "What is {emoji} TON?"; "lng_channel_earn_learn_coin_about" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}"; "lng_channel_earn_learn_close" = "Got it"; +"lng_channel_earn_learn_coin_link" = "https://telegram.org/blog/monetization-for-channels"; "lng_channel_earn_chart_top_hours" = "Ad impressions"; "lng_channel_earn_chart_revenue" = "Ad revenue"; "lng_channel_earn_chart_overriden_detail_currency" = "Revenue in TON"; diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp index 494f58f9a..19857a5ea 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp @@ -77,7 +77,7 @@ void ShowMenu(not_null box, const QString &text) { [[nodiscard]] ClickHandlerPtr LearnMoreCurrencyLink( not_null controller, not_null box) { - const auto url = u"https://telegram.org/blog/monetization-for-channels"_q; + const auto url = tr::lng_channel_earn_learn_coin_link(tr::now); using Resolver = HistoryView::Controls::WebpageResolver; const auto resolver = box->lifetime().make_state( From d675e31a510a5d6c38f5c386aba6b44348d594d5 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Apr 2024 18:37:26 +0300 Subject: [PATCH 25/71] Fixed display of integer numbers in channel earn section. --- .../SourceFiles/info/channel_statistics/earn/earn_format.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp index e79fa89ce..ea470af93 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/earn/earn_format.cpp @@ -12,6 +12,7 @@ namespace Info::ChannelEarn { using EarnInt = Data::EarnInt; constexpr auto kMinorPartLength = 9; +constexpr auto kMaxChoppedZero = kMinorPartLength - 2; constexpr auto kZero = QChar('0'); constexpr auto kDot = QChar('.'); @@ -35,7 +36,7 @@ QString MinorPart(EarnInt value) { auto ch = end - 1; auto zeroCount = 0; while (ch != begin) { - if ((*ch) == kZero) { + if (((*ch) == kZero) && (zeroCount < kMaxChoppedZero)) { zeroCount++; } else { break; From 2a388ae4b2d321012551cfc84218069200b6bd17 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Apr 2024 19:28:00 +0300 Subject: [PATCH 26/71] Improved FAQ buttons with opening link in instant view. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/data/data_birthday.cpp | 1 - Telegram/SourceFiles/data/data_faq.cpp | 53 +++++++++++++++++++ Telegram/SourceFiles/data/data_faq.h | 27 ++++++++++ Telegram/SourceFiles/data/data_session.h | 5 ++ .../SourceFiles/settings/settings_intro.cpp | 11 +++- .../SourceFiles/settings/settings_main.cpp | 37 +++++-------- Telegram/SourceFiles/settings/settings_main.h | 3 -- 9 files changed, 109 insertions(+), 31 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_faq.cpp create mode 100644 Telegram/SourceFiles/data/data_faq.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 4988f34b3..97337d1a4 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -508,6 +508,8 @@ PRIVATE data/data_drafts.h data/data_emoji_statuses.cpp data/data_emoji_statuses.h + data/data_faq.cpp + data/data_faq.h data/data_folder.cpp data/data_folder.h data/data_forum.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index fabd9238f..74b1518ab 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1068,6 +1068,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_faq_button" = "Go to FAQ"; "lng_settings_ask_ok" = "Ask a Volunteer"; "lng_settings_faq" = "Telegram FAQ"; +"lng_settings_faq_link" = "https://telegram.org/faq#general-questions"; "lng_settings_features" = "Telegram Features"; "lng_settings_logout" = "Log Out"; "lng_sure_logout" = "Are you sure you want to log out?"; diff --git a/Telegram/SourceFiles/data/data_birthday.cpp b/Telegram/SourceFiles/data/data_birthday.cpp index 65707664f..34a2b0737 100644 --- a/Telegram/SourceFiles/data/data_birthday.cpp +++ b/Telegram/SourceFiles/data/data_birthday.cpp @@ -127,4 +127,3 @@ rpl::producer IsBirthdayTodayValue(Birthday date) { } } // namespace Data - diff --git a/Telegram/SourceFiles/data/data_faq.cpp b/Telegram/SourceFiles/data/data_faq.cpp new file mode 100644 index 000000000..97ef86459 --- /dev/null +++ b/Telegram/SourceFiles/data/data_faq.cpp @@ -0,0 +1,53 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "data/data_faq.h" + +#include "apiwrap.h" +#include "core/application.h" +#include "data/data_session.h" +#include "data/data_web_page.h" +#include "iv/iv_instance.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "main/session/session_show.h" +#include "ui/basic_click_handlers.h" + +namespace Data { + +void Faq::open(std::shared_ptr show) { + if (!_id) { + show->session().api().request(MTPmessages_GetWebPage( + MTP_string(tr::lng_settings_faq_link(tr::now)), + MTP_int(0) + )).done([=](const MTPmessages_WebPage &result) { + show->session().data().processUsers(result.data().vusers()); + show->session().data().processChats(result.data().vchats()); + const auto page = show->session().data().processWebpage( + result.data().vwebpage()); + if (page && page->iv) { + _id = page->id; + open(show); + } else { + UrlClickHandler::Open(tr::lng_settings_faq_link(tr::now)); + } + }).fail([=] { + UrlClickHandler::Open(tr::lng_settings_faq_link(tr::now)); + }).send(); + } else { + const auto page = show->session().data().webpage(_id); + if (page && page->iv) { + const auto parts = tr::lng_settings_faq_link(tr::now).split('#'); + const auto hash = (parts.size() > 1) ? parts[1] : u""_q; + Core::App().iv().show(show, page->iv.get(), hash); + } else { + UrlClickHandler::Open(tr::lng_settings_faq_link(tr::now)); + } + } +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_faq.h b/Telegram/SourceFiles/data/data_faq.h new file mode 100644 index 000000000..7e0179c3b --- /dev/null +++ b/Telegram/SourceFiles/data/data_faq.h @@ -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 +*/ +#pragma once + +namespace Main { +class SessionShow; +} // namespace Main + +namespace Data { + +class Faq final { +public: + constexpr Faq() = default; + + void open(std::shared_ptr show); + +private: + WebPageId _id = 0; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 0f7df77f4..41aa27570 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_databases.h" #include "dialogs/dialogs_main_list.h" #include "data/data_groups.h" +#include "data/data_faq.h" #include "data/data_cloud_file.h" #include "history/history_location_manager.h" #include "base/timer.h" @@ -101,6 +102,9 @@ public: [[nodiscard]] const Groups &groups() const { return _groups; } + [[nodiscard]] Faq &faq() { + return _faq; + } [[nodiscard]] ChatFilters &chatsFilters() const { return *_chatsFilters; } @@ -1068,6 +1072,7 @@ private: mtpRequestId> _viewAsMessagesRequests; Groups _groups; + Faq _faq; const std::unique_ptr _chatsFilters; const std::unique_ptr _cloudThemes; const std::unique_ptr _sendActionManager; diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index bd38b8c3f..28665c913 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -11,10 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_main.h" #include "settings/settings_chat.h" #include "settings/settings_codes.h" +#include "ui/basic_click_handlers.h" #include "ui/wrap/fade_wrap.h" #include "ui/wrap/vertical_layout.h" #include "ui/widgets/shadow.h" -#include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/cached_round_corners.h" @@ -104,7 +104,14 @@ object_ptr CreateIntroSettings( Ui::AddDivider(result); Ui::AddSkip(result); - SetupFaq(result, false); + + AddButtonWithIcon( + result, + tr::lng_settings_faq(), + st::settingsButtonNoIcon + )->addClickHandler([] { + UrlClickHandler::Open(tr::lng_telegram_features_url(tr::now)); + }); return result; } diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 896a38a6b..4b2469b71 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -26,13 +26,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/basic_click_handlers.h" #include "ui/boxes/confirm_box.h" #include "ui/controls/userpic_button.h" -#include "ui/wrap/vertical_layout.h" #include "ui/wrap/slide_wrap.h" -#include "ui/wrap/padding_wrap.h" #include "ui/widgets/menu/menu_add_action_callback.h" -#include "ui/widgets/labels.h" #include "ui/widgets/continuous_sliders.h" -#include "ui/widgets/buttons.h" #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" #include "ui/new_badges.h" @@ -43,9 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_cloud_themes.h" #include "data/data_chat_filters.h" -#include "data/data_peer_values.h" // Data::AmPremiumValue #include "lang/lang_cloud_manager.h" -#include "lang/lang_keys.h" #include "lang/lang_instance.h" #include "storage/localstorage.h" #include "main/main_session.h" @@ -62,13 +56,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/profile/info_profile_values.h" #include "window/window_controller.h" #include "window/window_session_controller.h" -#include "core/file_utilities.h" -#include "core/application.h" #include "base/call_delayed.h" -#include "base/unixtime.h" #include "base/platform/base_platform_info.h" #include "styles/style_settings.h" -#include "styles/style_boxes.h" #include "styles/style_info.h" #include "styles/style_menu_icons.h" @@ -611,26 +601,20 @@ void SetupInterfaceScale( } } -void OpenFaq() { - File::OpenUrl(telegramFaqLink()); -} - -void SetupFaq(not_null container, bool icon) { - AddButtonWithIcon( - container, - tr::lng_settings_faq(), - icon ? st::settingsButton : st::settingsButtonNoIcon, - { icon ? &st::menuIconFaq : nullptr } - )->addClickHandler(OpenFaq); -} - void SetupHelp( not_null controller, not_null container) { Ui::AddDivider(container); Ui::AddSkip(container); - SetupFaq(container); + AddButtonWithIcon( + container, + tr::lng_settings_faq(), + st::settingsButton, + { &st::menuIconFaq } + )->addClickHandler([=] { + controller->session().data().faq().open(controller->uiShow()); + }); AddButtonWithIcon( container, @@ -674,7 +658,10 @@ void SetupHelp( auto box = Ui::MakeConfirmBox({ .text = tr::lng_settings_ask_sure(), .confirmed = sure, - .cancelled = OpenFaq, + .cancelled = [=](Fn close) { + controller->session().data().faq().open(controller->uiShow()); + close(); + }, .confirmText = tr::lng_settings_ask_ok(), .cancelText = tr::lng_settings_faq_button(), .strictCancel = true, diff --git a/Telegram/SourceFiles/settings/settings_main.h b/Telegram/SourceFiles/settings/settings_main.h index 7040fb9fa..303c58c5d 100644 --- a/Telegram/SourceFiles/settings/settings_main.h +++ b/Telegram/SourceFiles/settings/settings_main.h @@ -28,9 +28,6 @@ void SetupInterfaceScale( not_null window, not_null container, bool icon = true); -void SetupFaq( - not_null container, - bool icon = true); class Main : public Section
{ public: From b31d43cbed0e8e7097b170b0b5ab30acab0802b0 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Wed, 3 Apr 2024 19:31:52 +0300 Subject: [PATCH 27/71] Fixed circle drawing in titles of some sections in channel earn info. --- .../info/channel_statistics/earn/info_earn_inner_widget.cpp | 1 + Telegram/SourceFiles/menu/menu_sponsored.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp index 19857a5ea..80e377e1e 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_earn_inner_widget.cpp @@ -372,6 +372,7 @@ void InnerWidget::fill() { widget->paintRequest( ) | rpl::start_with_next([=] { auto p = Painter(widget); + auto hq = PainterHighQualityEnabler(p); p.setPen(Qt::NoPen); p.setBrush(st::activeButtonBg); p.drawEllipse(rect); diff --git a/Telegram/SourceFiles/menu/menu_sponsored.cpp b/Telegram/SourceFiles/menu/menu_sponsored.cpp index bb8109d2b..bfa8427f1 100644 --- a/Telegram/SourceFiles/menu/menu_sponsored.cpp +++ b/Telegram/SourceFiles/menu/menu_sponsored.cpp @@ -60,6 +60,7 @@ void AboutBox( widget->paintRequest( ) | rpl::start_with_next([=] { auto p = Painter(widget); + auto hq = PainterHighQualityEnabler(p); p.setPen(Qt::NoPen); p.setBrush(st::activeButtonBg); p.drawEllipse(rect); From 9464f4b095944b1fc657e6e8cc7ab59c2165ff48 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 17:45:15 +0400 Subject: [PATCH 28/71] Fix collectible channel links. --- .../info/profile/info_profile_actions.cpp | 13 +++++++------ .../info/profile/info_profile_values.cpp | 11 +++++++++-- .../SourceFiles/info/profile/info_profile_values.h | 8 +++++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index 84d8daba3..dfc7d3baa 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -1147,14 +1147,15 @@ object_ptr DetailsFiller::setupInfo() { auto linkText = LinkValue( _peer, true - ) | rpl::map([=](const QString &link) { - return link.isEmpty() + ) | rpl::map([=](const LinkWithUrl &link) { + const auto text = link.text; + return text.isEmpty() ? TextWithEntities() : Ui::Text::Link( - (link.startsWith(u"https://"_q) - ? link.mid(u"https://"_q.size()) - : link) + addToLink, - link + addToLink); + (text.startsWith(u"https://"_q) + ? text.mid(u"https://"_q.size()) + : text) + addToLink, + (addToLink.isEmpty() ? link.url : (text + addToLink))); }); auto linkLine = addInfoOneLine( (topicRootId diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 6159fe907..3a0f9566e 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -232,12 +232,19 @@ rpl::producer AboutValue(not_null peer) { }); } -rpl::producer LinkValue(not_null peer, bool primary) { +rpl::producer LinkValue(not_null peer, bool primary) { return (primary ? PlainPrimaryUsernameValue(peer) : PlainUsernameValue(peer) | rpl::type_erased() ) | rpl::map([=](QString &&username) { - return username.isEmpty() ? QString() : UsernameUrl(peer, username); + return LinkWithUrl{ + .text = (username.isEmpty() + ? QString() + : peer->session().createInternalLinkFull(username)), + .url = (username.isEmpty() + ? QString() + : UsernameUrl(peer, username)), + }; }); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.h b/Telegram/SourceFiles/info/profile/info_profile_values.h index 625ccfa2a..72d50055a 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.h +++ b/Telegram/SourceFiles/info/profile/info_profile_values.h @@ -69,9 +69,15 @@ rpl::producer> MigratedOrMeValue( const QString &value); [[nodiscard]] rpl::producer AboutValue( not_null peer); -[[nodiscard]] rpl::producer LinkValue( + +struct LinkWithUrl { + QString text; + QString url; +}; +[[nodiscard]] rpl::producer LinkValue( not_null peer, bool primary = false); + [[nodiscard]] rpl::producer LocationValue( not_null channel); [[nodiscard]] rpl::producer NotificationsEnabledValue( From 36cb28d5cf5a652e8de5788c567d63278102db20 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 18:20:33 +0400 Subject: [PATCH 29/71] Respect 'try_instant_view' in webapps. --- .../inline_bots/bot_attach_web_view.cpp | 49 +++++++++++++++++++ .../inline_bots/bot_attach_web_view.h | 5 ++ Telegram/SourceFiles/iv/iv_instance.cpp | 19 ++++--- Telegram/SourceFiles/iv/iv_instance.h | 4 ++ .../ui/chat/attach/attach_bot_webview.cpp | 6 ++- .../ui/chat/attach/attach_bot_webview.h | 1 + 6 files changed, 76 insertions(+), 8 deletions(-) diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index 056aa224e..c3f78b2d0 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -17,10 +17,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_session.h" +#include "data/data_web_page.h" #include "main/main_session.h" #include "main/main_domain.h" #include "storage/storage_domain.h" #include "info/profile/info_profile_values.h" +#include "iv/iv_instance.h" #include "ui/boxes/confirm_box.h" #include "ui/chat/attach/attach_bot_webview.h" #include "ui/widgets/checkbox.h" @@ -653,6 +655,53 @@ void AttachWebView::botHandleMenuButton(Ui::BotWebView::MenuButton button) { } } +void AttachWebView::botOpenIvLink(QString uri) { + const auto parts = uri.split('#'); + if (parts.isEmpty()) { + return; + } + const auto hash = (parts.size() > 1) ? parts[1] : u""_q; + const auto url = parts[0]; + if (const auto i = _ivCache.find(url); i != end(_ivCache)) { + const auto page = i->second; + if (page && page->iv) { + const auto window = _context + ? _context->controller.get() + : nullptr; + if (window) { + Core::App().iv().show(window, page->iv.get(), hash); + } else { + Core::App().iv().show(_session, page->iv.get(), hash); + } + } else { + UrlClickHandler::Open(uri); + } + return; + } else if (_ivRequestUri == uri) { + return; + } else if (_ivRequestId) { + _session->api().request(_ivRequestId).cancel(); + } + const auto finish = [=](WebPageData *page) { + _ivRequestId = 0; + _ivRequestUri = QString(); + _ivCache[url] = page; + botOpenIvLink(uri); + }; + _ivRequestUri = uri; + _ivRequestId = _session->api().request(MTPmessages_GetWebPage( + MTP_string(url), + MTP_int(0) + )).done([=](const MTPmessages_WebPage &result) { + const auto &data = result.data(); + _session->data().processUsers(data.vusers()); + _session->data().processChats(data.vchats()); + finish(_session->data().processWebpage(data.vwebpage())); + }).fail([=] { + finish(nullptr); + }).send(); +} + void AttachWebView::botSendData(QByteArray data) { if (!_context || _context->fromSwitch diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index d1d2c0e44..c172cd5c3 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -165,6 +165,7 @@ private: bool botHandleLocalUri(QString uri, bool keepOpen) override; void botHandleInvoice(QString slug) override; void botHandleMenuButton(Ui::BotWebView::MenuButton button) override; + void botOpenIvLink(QString uri) override; void botSendData(QByteArray data) override; void botSwitchInlineQuery( std::vector chatTypes, @@ -266,6 +267,10 @@ private: rpl::event_stream<> _attachBotsUpdates; base::flat_set> _disclaimerAccepted; + base::flat_map _ivCache; + QString _ivRequestUri; + mtpRequestId _ivRequestId = 0; + std::unique_ptr _panel; bool _catchingCancelInShowCall = false; diff --git a/Telegram/SourceFiles/iv/iv_instance.cpp b/Telegram/SourceFiles/iv/iv_instance.cpp index 517415435..f84d55223 100644 --- a/Telegram/SourceFiles/iv/iv_instance.cpp +++ b/Telegram/SourceFiles/iv/iv_instance.cpp @@ -63,7 +63,7 @@ class Shown final : public base::has_weak_ptr { public: Shown( not_null delegate, - std::shared_ptr show, + not_null session, not_null data, QString hash); @@ -169,12 +169,11 @@ private: Shown::Shown( not_null delegate, - std::shared_ptr show, + not_null session, not_null data, QString hash) : _delegate(delegate) -, _session(&show->session()) -, _show(show) { +, _session(session) { prepare(data, hash); } @@ -818,7 +817,13 @@ void Instance::show( std::shared_ptr show, not_null data, QString hash) { - const auto session = &show->session(); + this->show(&show->session(), data, hash); +} + +void Instance::show( + not_null session, + not_null data, + QString hash) { const auto guard = gsl::finally([&] { if (data->partial()) { requestFull(session, data->id()); @@ -828,7 +833,7 @@ void Instance::show( _shown->moveTo(data, hash); return; } - _shown = std::make_unique(_delegate, show, data, hash); + _shown = std::make_unique(_delegate, session, data, hash); _shownSession = session; _shown->events() | rpl::start_with_next([=](Controller::Event event) { using Type = Controller::Event::Type; @@ -896,7 +901,7 @@ void Instance::show( if (page && page->iv) { const auto parts = event.url.split('#'); const auto hash = (parts.size() > 1) ? parts[1] : u""_q; - this->show(show, page->iv.get(), hash); + this->show(_shownSession, page->iv.get(), hash); } else { UrlClickHandler::Open(event.url); } diff --git a/Telegram/SourceFiles/iv/iv_instance.h b/Telegram/SourceFiles/iv/iv_instance.h index ba3d8f72c..608c9c283 100644 --- a/Telegram/SourceFiles/iv/iv_instance.h +++ b/Telegram/SourceFiles/iv/iv_instance.h @@ -36,6 +36,10 @@ public: std::shared_ptr show, not_null data, QString hash); + void show( + not_null session, + not_null data, + QString hash); [[nodiscard]] bool hasActiveWindow( not_null session) const; diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index 42eac21de..c9f3f93f8 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -801,6 +801,7 @@ void Panel::openExternalLink(const QJsonObject &args) { return; } const auto url = args["url"].toString(); + const auto iv = args["try_instant_view"].toBool(); const auto lower = url.toLower(); if (url.isEmpty() || (!lower.startsWith("http://") && !lower.startsWith("https://"))) { @@ -809,8 +810,11 @@ void Panel::openExternalLink(const QJsonObject &args) { return; } else if (!allowOpenLink()) { return; + } else if (iv) { + _delegate->botOpenIvLink(url); + } else { + File::OpenUrl(url); } - File::OpenUrl(url); } void Panel::openInvoice(const QJsonObject &args) { diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h index 9c4be46d0..bb22a4dfc 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.h @@ -57,6 +57,7 @@ public: virtual bool botHandleLocalUri(QString uri, bool keepOpen) = 0; virtual void botHandleInvoice(QString slug) = 0; virtual void botHandleMenuButton(MenuButton button) = 0; + virtual void botOpenIvLink(QString uri) = 0; virtual void botSendData(QByteArray data) = 0; virtual void botSwitchInlineQuery( std::vector chatTypes, From ede6e18bc9064801e62ba547b7bb9f6ea48fe056 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 18:32:01 +0400 Subject: [PATCH 30/71] Fix QOI image reader. --- cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake b/cmake index b875d0ae2..9f1b85aa9 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit b875d0ae2bf457ec94aaddd08431753e14ec71ac +Subproject commit 9f1b85aa9d51025ccefe63992d05c878bc267774 From f6f8eefaa0e2f149c031fd83bcf0ee3f5759546e Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 18:36:00 +0400 Subject: [PATCH 31/71] Revert "Use plain vector for prepared upload parts." This reverts commit aaaeea297927211fe4e5ea09ce76b324eae24920. --- Telegram/SourceFiles/storage/file_upload.cpp | 160 ++++++++++-------- Telegram/SourceFiles/storage/file_upload.h | 14 +- .../SourceFiles/storage/localimageloader.cpp | 8 +- .../SourceFiles/storage/localimageloader.h | 2 +- 4 files changed, 96 insertions(+), 88 deletions(-) diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index b19af82df..e0c7f8b57 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -65,10 +65,7 @@ struct Uploader::File { bool setPartSize(uint32 partSize); std::shared_ptr file; - const std::vector &parts; - const uint64 partsOfId = 0; - int partsSent = 0; - + int32 partsCount = 0; mutable int64 fileSentSize = 0; HashMd5 md5Hash; @@ -82,15 +79,11 @@ struct Uploader::File { }; Uploader::File::File(const std::shared_ptr &file) -: file(file) -, parts((file->type == SendMediaType::Photo - || file->type == SendMediaType::Secure) - ? file->fileparts - : file->thumbparts) -, partsOfId((file->type == SendMediaType::Photo - || file->type == SendMediaType::Secure) - ? file->id - : file->thumbId) { +: file(file) { + partsCount = (file->type == SendMediaType::Photo + || file->type == SendMediaType::Secure) + ? file->fileparts.size() + : file->thumbparts.size(); if (file->type == SendMediaType::File || file->type == SendMediaType::ThemeFile || file->type == SendMediaType::Audio) { @@ -181,7 +174,7 @@ Uploader::Uploader(not_null api) _api->instance().nonPremiumDelayedRequests( ) | rpl::start_with_next([=](mtpRequestId id) { - if (_dcIndices.contains(id)) { + if (dcMap.contains(id)) { _nonPremiumDelayed.emplace(id); } }, _lifetime); @@ -306,7 +299,7 @@ void Uploader::upload( } void Uploader::currentFailed() { - auto j = queue.find(_uploadingId); + auto j = queue.find(uploadingId); if (j != queue.end()) { const auto [msgId, file] = std::move(*j); queue.erase(j); @@ -314,11 +307,11 @@ void Uploader::currentFailed() { } cancelRequests(); - _dcIndices.clear(); - _uploadingId = FullMsgId(); - _sentTotal = 0; + dcMap.clear(); + uploadingId = FullMsgId(); + sentSize = 0; for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { - _sentPerDc[i] = 0; + sentSizes[i] = 0; } sendNext(); @@ -350,7 +343,7 @@ void Uploader::stopSessions() { } void Uploader::sendNext() { - if (_sentTotal >= kMaxUploadFileParallelSize || _pausedId.msg) { + if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) { return; } @@ -365,25 +358,33 @@ void Uploader::sendNext() { if (stopping) { _stopSessionsTimer.cancel(); } - auto i = _uploadingId.msg ? queue.find(_uploadingId) : queue.begin(); - if (!_uploadingId.msg) { - _uploadingId = i->first; + auto i = uploadingId.msg ? queue.find(uploadingId) : queue.begin(); + if (!uploadingId.msg) { + uploadingId = i->first; } else if (i == queue.end()) { i = queue.begin(); - _uploadingId = i->first; + uploadingId = i->first; } auto &uploadingData = i->second; auto todc = 0; for (auto dc = 1; dc != MTP::kUploadSessionsCount; ++dc) { - if (_sentPerDc[dc] < _sentPerDc[todc]) { + if (sentSizes[dc] < sentSizes[todc]) { todc = dc; } } - if (uploadingData.partsSent >= uploadingData.parts.size()) { + auto &parts = (uploadingData.file->type == SendMediaType::Photo + || uploadingData.file->type == SendMediaType::Secure) + ? uploadingData.file->fileparts + : uploadingData.file->thumbparts; + const auto partsOfId = (uploadingData.file->type == SendMediaType::Photo + || uploadingData.file->type == SendMediaType::Secure) + ? uploadingData.file->id + : uploadingData.file->thumbId; + if (parts.isEmpty()) { if (uploadingData.docSentParts >= uploadingData.docPartsCount) { - if (_sentSizes.empty()) { + if (requestsSent.empty() && docRequestsSent.empty()) { const auto options = uploadingData.file ? uploadingData.file->to.options : Api::SendOptions(); @@ -403,11 +404,11 @@ void Uploader::sendNext() { const auto md5 = uploadingData.file->filemd5; const auto file = MTP_inputFile( MTP_long(uploadingData.file->id), - MTP_int(uploadingData.parts.size()), + MTP_int(uploadingData.partsCount), MTP_string(photoFilename), MTP_bytes(md5)); _photoReady.fire({ - .fullId = _uploadingId, + .fullId = uploadingId, .info = { .file = file, .attachedStickers = attachedStickers, @@ -432,19 +433,19 @@ void Uploader::sendNext() { MTP_string(uploadingData.file->filename), MTP_bytes(docMd5)); const auto thumb = [&]() -> std::optional { - if (uploadingData.parts.empty()) { + if (!uploadingData.partsCount) { return std::nullopt; } const auto thumbFilename = uploadingData.file->thumbname; const auto thumbMd5 = uploadingData.file->thumbmd5; return MTP_inputFile( MTP_long(uploadingData.file->thumbId), - MTP_int(uploadingData.parts.size()), + MTP_int(uploadingData.partsCount), MTP_string(thumbFilename), MTP_bytes(thumbMd5)); }(); _documentReady.fire({ - .fullId = _uploadingId, + .fullId = uploadingId, .info = { .file = file, .thumb = thumb, @@ -455,13 +456,12 @@ void Uploader::sendNext() { }); } else if (uploadingData.file->type == SendMediaType::Secure) { _secureReady.fire({ - _uploadingId, + uploadingId, uploadingData.file->id, - int(uploadingData.parts.size()), - }); + uploadingData.partsCount }); } - queue.erase(_uploadingId); - _uploadingId = FullMsgId(); + queue.erase(uploadingId); + uploadingId = FullMsgId(); sendNext(); } return; @@ -522,37 +522,36 @@ void Uploader::sendNext() { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); } - _sentSizes.emplace(requestId, uploadingData.docPartSize); - _docSentRequests.emplace(requestId); - _dcIndices.emplace(requestId, todc); - _sentTotal += uploadingData.docPartSize; - _sentPerDc[todc] += uploadingData.docPartSize; + docRequestsSent.emplace(requestId, uploadingData.docSentParts); + dcMap.emplace(requestId, todc); + sentSize += uploadingData.docPartSize; + sentSizes[todc] += uploadingData.docPartSize; uploadingData.docSentParts++; } else { - const auto index = uploadingData.partsSent++; - const auto partBytes = uploadingData.parts[index]; - const auto partSize = int(partBytes.size()); + auto part = parts.begin(); const auto requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(uploadingData.partsOfId), - MTP_int(index), - MTP_bytes(partBytes) + MTP_long(partsOfId), + MTP_int(part.key()), + MTP_bytes(part.value()) )).done([=](const MTPBool &result, mtpRequestId requestId) { partLoaded(result, requestId); }).fail([=](const MTP::Error &error, mtpRequestId requestId) { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); - _sentSizes.emplace(requestId, partSize); - _dcIndices.emplace(requestId, todc); - _sentTotal += partSize; - _sentPerDc[todc] += partSize; + requestsSent.emplace(requestId, part.value()); + dcMap.emplace(requestId, todc); + sentSize += part.value().size(); + sentSizes[todc] += part.value().size(); + + parts.erase(part); } _nextTimer.callOnce(kUploadRequestInterval); } void Uploader::cancel(const FullMsgId &msgId) { - if (_uploadingId == msgId) { + if (uploadingId == msgId) { currentFailed(); } else { queue.erase(msgId); @@ -560,12 +559,12 @@ void Uploader::cancel(const FullMsgId &msgId) { } void Uploader::cancelAll() { - const auto single = queue.empty() ? _uploadingId : queue.begin()->first; + const auto single = queue.empty() ? uploadingId : queue.begin()->first; if (!single) { return; } _pausedId = single; - if (_uploadingId) { + if (uploadingId) { currentFailed(); } while (!queue.empty()) { @@ -590,49 +589,61 @@ void Uploader::confirm(const FullMsgId &msgId) { } void Uploader::cancelRequests() { - _docSentRequests.clear(); - for (const auto &requestData : _sentSizes) { + for (const auto &requestData : requestsSent) { _api->request(requestData.first).cancel(); } - _sentSizes.clear(); + requestsSent.clear(); + for (const auto &requestData : docRequestsSent) { + _api->request(requestData.first).cancel(); + } + docRequestsSent.clear(); } void Uploader::clear() { queue.clear(); cancelRequests(); - _dcIndices.clear(); - _sentTotal = 0; + dcMap.clear(); + sentSize = 0; for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { _api->instance().stopSession(MTP::uploadDcId(i)); - _sentPerDc[i] = 0; + sentSizes[i] = 0; } _stopSessionsTimer.cancel(); } void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { - _docSentRequests.remove(requestId); - auto i = _sentSizes.find(requestId); + auto j = docRequestsSent.end(); + auto i = requestsSent.find(requestId); + if (i == requestsSent.cend()) { + j = docRequestsSent.find(requestId); + } const auto wasNonPremiumDelayed = _nonPremiumDelayed.remove(requestId); - if (i != _sentSizes.cend()) { + if (i != requestsSent.cend() || j != docRequestsSent.cend()) { if (mtpIsFalse(result)) { // failed to upload current file currentFailed(); return; } else { - auto dcIt = _dcIndices.find(requestId); - if (dcIt == _dcIndices.cend()) { // must not happen + auto dcIt = dcMap.find(requestId); + if (dcIt == dcMap.cend()) { // must not happen currentFailed(); return; } auto dc = dcIt->second; - _dcIndices.erase(dcIt); + dcMap.erase(dcIt); - int64 sentPartSize = i->second; - auto k = queue.find(_uploadingId); + int64 sentPartSize = 0; + auto k = queue.find(uploadingId); Assert(k != queue.cend()); auto &[fullId, file] = *k; - _sentSizes.erase(i); - _sentTotal -= sentPartSize; - _sentPerDc[dc] -= sentPartSize; + if (i != requestsSent.cend()) { + sentPartSize = i->second.size(); + requestsSent.erase(i); + } else { + sentPartSize = file.docPartSize; + docRequestsSent.erase(j); + } + sentSize -= sentPartSize; + sentSizes[dc] -= sentPartSize; if (file.file->type == SendMediaType::Photo) { file.fileSentSize += sentPartSize; const auto photo = session().data().photo(file.file->id); @@ -647,7 +658,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { const auto document = session().data().document(file.file->id); if (document->uploading()) { const auto doneParts = file.docSentParts - - int(_docSentRequests.size()); + - int(docRequestsSent.size()); document->uploadingData->offset = std::min( document->uploadingData->size, doneParts * file.docPartSize); @@ -672,7 +683,8 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { void Uploader::partFailed(const MTP::Error &error, mtpRequestId requestId) { // failed to upload current file _nonPremiumDelayed.remove(requestId); - if (_sentSizes.find(requestId) != _sentSizes.cend()) { + if ((requestsSent.find(requestId) != requestsSent.cend()) + || (docRequestsSent.find(requestId) != docRequestsSent.cend())) { currentFailed(); } sendNext(); diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index a733775ad..4347e5c03 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -54,7 +54,7 @@ public: [[nodiscard]] Main::Session &session() const; [[nodiscard]] FullMsgId currentUploadId() const { - return _uploadingId; + return uploadingId; } void upload( @@ -125,14 +125,14 @@ private: int progress = 0); const not_null _api; - base::flat_map _sentSizes; - base::flat_set _docSentRequests; - base::flat_map _dcIndices; + base::flat_map requestsSent; + base::flat_map docRequestsSent; + base::flat_map dcMap; base::flat_set _nonPremiumDelayed; - uint32 _sentTotal = 0; // FileSize: Right now any file size fits 32 bit. - uint32 _sentPerDc[MTP::kUploadSessionsCount] = { 0 }; + uint32 sentSize = 0; // FileSize: Right now any file size fits 32 bit. + uint32 sentSizes[MTP::kUploadSessionsCount] = { 0 }; - FullMsgId _uploadingId; + FullMsgId uploadingId; FullMsgId _pausedId; std::map queue; base::Timer _nextTimer, _stopSessionsTimer; diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 59f2fb505..192c7c112 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -434,10 +434,8 @@ void FilePrepareResult::setFileData(const QByteArray &filedata) { partssize = 0; } else { partssize = filedata.size(); - fileparts.reserve( - (partssize + kPhotoUploadPartSize - 1) / kPhotoUploadPartSize); for (int32 i = 0, part = 0; i < partssize; i += kPhotoUploadPartSize, ++part) { - fileparts.push_back(filedata.mid(i, kPhotoUploadPartSize)); + fileparts.insert(part, filedata.mid(i, kPhotoUploadPartSize)); } filemd5.resize(32); hashMd5Hex(filedata.constData(), filedata.size(), filemd5.data()); @@ -448,10 +446,8 @@ void FilePrepareResult::setThumbData(const QByteArray &thumbdata) { if (!thumbdata.isEmpty()) { thumbbytes = thumbdata; int32 size = thumbdata.size(); - thumbparts.reserve( - (size + kPhotoUploadPartSize - 1) / kPhotoUploadPartSize); for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) { - thumbparts.push_back(thumbdata.mid(i, kPhotoUploadPartSize)); + thumbparts.insert(part, thumbdata.mid(i, kPhotoUploadPartSize)); } thumbmd5.resize(32); hashMd5Hex(thumbdata.constData(), thumbdata.size(), thumbmd5.data()); diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 2756ac244..8b3b7bca7 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -147,7 +147,7 @@ struct FileLoadTo { MsgId replaceMediaOf; }; -using UploadFileParts = std::vector; +using UploadFileParts = QMap; struct FilePrepareDescriptor { TaskId taskId = kEmptyTaskId; base::required id; From 09f07a7a9dde2c2b58c5a29fa6c20a95f4522dea Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 18:36:07 +0400 Subject: [PATCH 32/71] Revert "Remove SendMediaReady legacy helper." This reverts commit 91f8989f701c1c3bfe299ac8e653efc7d22a38e3. --- Telegram/SourceFiles/api/api_peer_photo.cpp | 33 ++-- Telegram/SourceFiles/api/api_ringtones.cpp | 33 ++-- Telegram/SourceFiles/api/api_sending.cpp | 2 +- Telegram/SourceFiles/api/api_sending.h | 4 +- .../boxes/background_preview_box.cpp | 4 +- Telegram/SourceFiles/history/history_widget.h | 1 + Telegram/SourceFiles/iv/iv_data.cpp | 2 +- Telegram/SourceFiles/mainwidget.h | 1 + .../passport/passport_form_controller.cpp | 12 +- Telegram/SourceFiles/storage/file_upload.cpp | 177 +++++++++++++----- Telegram/SourceFiles/storage/file_upload.h | 6 +- .../SourceFiles/storage/localimageloader.cpp | 83 +++++--- .../SourceFiles/storage/localimageloader.h | 74 +++++--- .../window/themes/window_theme.cpp | 41 ++-- .../SourceFiles/window/themes/window_theme.h | 4 +- .../window/themes/window_theme_editor_box.cpp | 60 +++--- 16 files changed, 354 insertions(+), 183 deletions(-) diff --git a/Telegram/SourceFiles/api/api_peer_photo.cpp b/Telegram/SourceFiles/api/api_peer_photo.cpp index 9c8a470fa..c70ef4491 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.cpp +++ b/Telegram/SourceFiles/api/api_peer_photo.cpp @@ -34,7 +34,7 @@ namespace { constexpr auto kSharedMediaLimit = 100; -[[nodiscard]] std::shared_ptr PreparePeerPhoto( +[[nodiscard]] SendMediaReady PreparePeerPhoto( MTP::DcId dcId, PeerId peerId, QImage &&image) { @@ -80,17 +80,24 @@ constexpr auto kSharedMediaLimit = 100; MTPVector(), MTP_int(dcId)); - auto result = MakePreparedFile({ - .id = id, - .type = SendMediaType::Photo, - }); - result->type = SendMediaType::Photo; - result->setFileData(jpeg); - result->thumbId = id; - result->thumbname = "thumb.jpg"; - result->photo = photo; - result->photoThumbs = photoThumbs; - return result; + QString file, filename; + int64 filesize = 0; + QByteArray data; + + return SendMediaReady( + SendMediaType::Photo, + file, + filename, + filesize, + data, + id, + id, + u"jpg"_q, + peerId, + photo, + photoThumbs, + MTP_documentEmpty(MTP_long(0)), + jpeg); } [[nodiscard]] std::optional PrepareMtpMarkup( @@ -232,7 +239,7 @@ void PeerPhoto::upload( _api.instance().mainDcId(), peer->id, base::take(photo.image)); - _session->uploader().upload(fakeId, ready); + _session->uploader().uploadMedia(fakeId, ready); } } diff --git a/Telegram/SourceFiles/api/api_ringtones.cpp b/Telegram/SourceFiles/api/api_ringtones.cpp index 307ba580a..87522bf58 100644 --- a/Telegram/SourceFiles/api/api_ringtones.cpp +++ b/Telegram/SourceFiles/api/api_ringtones.cpp @@ -24,25 +24,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Api { namespace { -std::shared_ptr PrepareRingtoneDocument( +SendMediaReady PrepareRingtoneDocument( MTP::DcId dcId, const QString &filename, const QString &filemime, const QByteArray &content) { - const auto id = base::RandomValue(); auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - - auto result = MakePreparedFile({ - .id = id, - .type = SendMediaType::File, - }); - result->filename = filename; - result->content = content; - result->filesize = content.size(); - result->setFileData(content); - result->document = MTP_document( + const auto id = base::RandomValue(); + const auto document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -54,7 +45,21 @@ std::shared_ptr PrepareRingtoneDocument( MTPVector(), MTP_int(dcId), MTP_vector(std::move(attributes))); - return result; + + return SendMediaReady( + SendMediaType::File, + QString(), // filepath + filename, + content.size(), + content, + id, + 0, + QString(), + PeerId(), + MTP_photoEmpty(MTP_long(0)), + PreparedPhotoThumbs(), + document, + QByteArray()); } } // namespace @@ -97,7 +102,7 @@ void Ringtones::upload( _uploads.erase(already); } _uploads.emplace(fakeId, uploadedData); - _session->uploader().upload(fakeId, ready); + _session->uploader().uploadMedia(fakeId, ready); } void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) { diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index d91943fe0..72bd06b3d 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -353,7 +353,7 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file) { + const std::shared_ptr &file) { const auto isEditing = (file->type != SendMediaType::Audio) && (file->to.replaceMediaOf != 0); const auto newId = FullMsgId( diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h index 2fdbad843..e17c66f3e 100644 --- a/Telegram/SourceFiles/api/api_sending.h +++ b/Telegram/SourceFiles/api/api_sending.h @@ -14,7 +14,7 @@ class Session; class History; class PhotoData; class DocumentData; -struct FilePrepareResult; +struct FileLoadResult; namespace Api { @@ -40,6 +40,6 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file); + const std::shared_ptr &file); } // namespace Api diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index a112c478a..3a9917719 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -593,11 +593,11 @@ void BackgroundPreviewBox::uploadForPeer(bool both) { const auto ready = Window::Theme::PrepareWallPaper( session->mainDcId(), _paper.localThumbnail()->original()); - const auto documentId = ready->id; + const auto documentId = ready.id; _uploadId = FullMsgId( session->userPeerId(), session->data().nextLocalMessageId()); - session->uploader().upload(_uploadId, ready); + session->uploader().uploadMedia(_uploadId, ready); if (_uploadLifetime) { return; } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index b4d8bb49c..43966331e 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/fields/input_field.h" #include "mtproto/sender.h" +struct FileLoadResult; enum class SendMediaType; class MessageLinksParser; struct InlineBotQuery; diff --git a/Telegram/SourceFiles/iv/iv_data.cpp b/Telegram/SourceFiles/iv/iv_data.cpp index 5258e6c9f..bbcca0b2c 100644 --- a/Telegram/SourceFiles/iv/iv_data.cpp +++ b/Telegram/SourceFiles/iv/iv_data.cpp @@ -25,7 +25,7 @@ QByteArray GeoPointId(Geo point) { const auto lon = int(point.lon * 1000000); const auto combined = (std::uint64_t(std::uint32_t(lat)) << 32) | std::uint64_t(std::uint32_t(lon)); - return QByteArray::number(quint64(combined)) + return QByteArray::number(combined) + ',' + QByteArray::number(point.access); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 809086765..10bea63d4 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -16,6 +16,7 @@ struct HistoryMessageMarkupButton; class MainWindow; class HistoryWidget; class StackItem; +struct FileLoadResult; class History; class Image; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 980888ffb..684163163 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -1581,10 +1581,14 @@ void FormController::uploadEncryptedFile( &session(), std::make_unique(std::move(data))); - auto prepared = MakePreparedFile({ - .id = file.uploadData->fileId, - .type = SendMediaType::Secure, - }); + auto prepared = std::make_shared( + TaskId(), + file.uploadData->fileId, + FileLoadTo(PeerId(), Api::SendOptions(), FullReplyTo(), MsgId()), + TextWithTags(), + false, + std::shared_ptr(nullptr)); + prepared->type = SendMediaType::Secure; prepared->content = QByteArray::fromRawData( reinterpret_cast(file.uploadData->bytes.data()), file.uploadData->bytes.size()); diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index e0c7f8b57..c40396955 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -59,15 +59,22 @@ constexpr auto kKillSessionTimeout = 15 * crl::time(1000); } // namespace struct Uploader::File { - explicit File(const std::shared_ptr &file); + File(const SendMediaReady &media); + File(const std::shared_ptr &file); void setDocSize(int64 size); bool setPartSize(uint32 partSize); - std::shared_ptr file; + std::shared_ptr file; + SendMediaReady media; int32 partsCount = 0; mutable int64 fileSentSize = 0; + uint64 id() const; + SendMediaType type() const; + uint64 thumbId() const; + const QString &filename() const; + HashMd5 md5Hash; std::unique_ptr docFile; @@ -78,15 +85,27 @@ struct Uploader::File { }; -Uploader::File::File(const std::shared_ptr &file) +Uploader::File::File(const SendMediaReady &media) : media(media) { + partsCount = media.parts.size(); + if (type() == SendMediaType::File + || type() == SendMediaType::ThemeFile + || type() == SendMediaType::Audio) { + setDocSize(media.file.isEmpty() + ? media.data.size() + : media.filesize); + } else { + docSize = docPartSize = docPartsCount = 0; + } +} +Uploader::File::File(const std::shared_ptr &file) : file(file) { - partsCount = (file->type == SendMediaType::Photo - || file->type == SendMediaType::Secure) + partsCount = (type() == SendMediaType::Photo + || type() == SendMediaType::Secure) ? file->fileparts.size() : file->thumbparts.size(); - if (file->type == SendMediaType::File - || file->type == SendMediaType::ThemeFile - || file->type == SendMediaType::Audio) { + if (type() == SendMediaType::File + || type() == SendMediaType::ThemeFile + || type() == SendMediaType::Audio) { setDocSize(file->filesize); } else { docSize = docPartSize = docPartsCount = 0; @@ -115,6 +134,22 @@ bool Uploader::File::setPartSize(uint32 partSize) { return (docPartsCount <= kDocumentMaxPartsCountDefault); } +uint64 Uploader::File::id() const { + return file ? file->id : media.id; +} + +SendMediaType Uploader::File::type() const { + return file ? file->type : media.type; +} + +uint64 Uploader::File::thumbId() const { + return file ? file->thumbId : media.thumbId; +} + +const QString &Uploader::File::filename() const { + return file ? file->filename : media.filename; +} + Uploader::Uploader(not_null api) : _api(api) , _nextTimer([=] { sendNext(); }) @@ -247,9 +282,39 @@ Main::Session &Uploader::session() const { return _api->session(); } +void Uploader::uploadMedia( + const FullMsgId &msgId, + const SendMediaReady &media) { + if (media.type == SendMediaType::Photo) { + session().data().processPhoto(media.photo, media.photoThumbs); + } else if (media.type == SendMediaType::File + || media.type == SendMediaType::ThemeFile + || media.type == SendMediaType::Audio) { + const auto document = media.photoThumbs.empty() + ? session().data().processDocument(media.document) + : session().data().processDocument( + media.document, + Images::FromImageInMemory( + media.photoThumbs.front().second.image, + "JPG", + media.photoThumbs.front().second.bytes)); + if (!media.data.isEmpty()) { + document->setDataAndCache(media.data); + if (media.type == SendMediaType::ThemeFile) { + document->checkWallPaperProperties(); + } + } + if (!media.file.isEmpty()) { + document->setLocation(Core::FileLocation(media.file)); + } + } + queue.emplace(msgId, File(media)); + sendNext(); +} + void Uploader::upload( const FullMsgId &msgId, - const std::shared_ptr &file) { + const std::shared_ptr &file) { if (file->type == SendMediaType::Photo) { const auto photo = session().data().processPhoto( file->photo, @@ -318,13 +383,13 @@ void Uploader::currentFailed() { } void Uploader::notifyFailed(FullMsgId id, const File &file) { - const auto type = file.file->type; + const auto type = file.type(); if (type == SendMediaType::Photo) { _photoFailed.fire_copy(id); } else if (type == SendMediaType::File || type == SendMediaType::ThemeFile || type == SendMediaType::Audio) { - const auto document = session().data().document(file.file->id); + const auto document = session().data().document(file.id()); if (document->uploading()) { document->status = FileUploadFailed; } @@ -374,14 +439,18 @@ void Uploader::sendNext() { } } - auto &parts = (uploadingData.file->type == SendMediaType::Photo - || uploadingData.file->type == SendMediaType::Secure) - ? uploadingData.file->fileparts - : uploadingData.file->thumbparts; - const auto partsOfId = (uploadingData.file->type == SendMediaType::Photo - || uploadingData.file->type == SendMediaType::Secure) - ? uploadingData.file->id - : uploadingData.file->thumbId; + auto &parts = uploadingData.file + ? ((uploadingData.type() == SendMediaType::Photo + || uploadingData.type() == SendMediaType::Secure) + ? uploadingData.file->fileparts + : uploadingData.file->thumbparts) + : uploadingData.media.parts; + const auto partsOfId = uploadingData.file + ? ((uploadingData.type() == SendMediaType::Photo + || uploadingData.type() == SendMediaType::Secure) + ? uploadingData.file->id + : uploadingData.file->thumbId) + : uploadingData.media.thumbId; if (parts.isEmpty()) { if (uploadingData.docSentParts >= uploadingData.docPartsCount) { if (requestsSent.empty() && docRequestsSent.empty()) { @@ -393,17 +462,19 @@ void Uploader::sendNext() { const auto attachedStickers = uploadingData.file ? uploadingData.file->attachedStickers : std::vector(); - if (uploadingData.file->type == SendMediaType::Photo) { - auto photoFilename = uploadingData.file->filename; + if (uploadingData.type() == SendMediaType::Photo) { + auto photoFilename = uploadingData.filename(); if (!photoFilename.endsWith(u".jpg"_q, Qt::CaseInsensitive)) { // Server has some extensions checking for inputMediaUploadedPhoto, // so force the extension to be .jpg anyway. It doesn't matter, // because the filename from inputFile is not used anywhere. photoFilename += u".jpg"_q; } - const auto md5 = uploadingData.file->filemd5; + const auto md5 = uploadingData.file + ? uploadingData.file->filemd5 + : uploadingData.media.jpeg_md5; const auto file = MTP_inputFile( - MTP_long(uploadingData.file->id), + MTP_long(uploadingData.id()), MTP_int(uploadingData.partsCount), MTP_string(photoFilename), MTP_bytes(md5)); @@ -416,30 +487,34 @@ void Uploader::sendNext() { .options = options, .edit = edit, }); - } else if (uploadingData.file->type == SendMediaType::File - || uploadingData.file->type == SendMediaType::ThemeFile - || uploadingData.file->type == SendMediaType::Audio) { + } else if (uploadingData.type() == SendMediaType::File + || uploadingData.type() == SendMediaType::ThemeFile + || uploadingData.type() == SendMediaType::Audio) { QByteArray docMd5(32, Qt::Uninitialized); hashMd5Hex(uploadingData.md5Hash.result(), docMd5.data()); const auto file = (uploadingData.docSize > kUseBigFilesFrom) ? MTP_inputFileBig( - MTP_long(uploadingData.file->id), + MTP_long(uploadingData.id()), MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.file->filename)) + MTP_string(uploadingData.filename())) : MTP_inputFile( - MTP_long(uploadingData.file->id), + MTP_long(uploadingData.id()), MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.file->filename), + MTP_string(uploadingData.filename()), MTP_bytes(docMd5)); const auto thumb = [&]() -> std::optional { if (!uploadingData.partsCount) { return std::nullopt; } - const auto thumbFilename = uploadingData.file->thumbname; - const auto thumbMd5 = uploadingData.file->thumbmd5; + const auto thumbFilename = uploadingData.file + ? uploadingData.file->thumbname + : (u"thumb."_q + uploadingData.media.thumbExt); + const auto thumbMd5 = uploadingData.file + ? uploadingData.file->thumbmd5 + : uploadingData.media.jpeg_md5; return MTP_inputFile( - MTP_long(uploadingData.file->thumbId), + MTP_long(uploadingData.thumbId()), MTP_int(uploadingData.partsCount), MTP_string(thumbFilename), MTP_bytes(thumbMd5)); @@ -454,10 +529,10 @@ void Uploader::sendNext() { .options = options, .edit = edit, }); - } else if (uploadingData.file->type == SendMediaType::Secure) { + } else if (uploadingData.type() == SendMediaType::Secure) { _secureReady.fire({ uploadingId, - uploadingData.file->id, + uploadingData.id(), uploadingData.partsCount }); } queue.erase(uploadingId); @@ -467,11 +542,15 @@ void Uploader::sendNext() { return; } - auto &content = uploadingData.file->content; + auto &content = uploadingData.file + ? uploadingData.file->content + : uploadingData.media.data; QByteArray toSend; if (content.isEmpty()) { if (!uploadingData.docFile) { - const auto filepath = uploadingData.file->filepath; + const auto filepath = uploadingData.file + ? uploadingData.file->filepath + : uploadingData.media.file; uploadingData.docFile = std::make_unique(filepath); if (!uploadingData.docFile->open(QIODevice::ReadOnly)) { currentFailed(); @@ -486,9 +565,9 @@ void Uploader::sendNext() { const auto offset = uploadingData.docSentParts * uploadingData.docPartSize; toSend = content.mid(offset, uploadingData.docPartSize); - if ((uploadingData.file->type == SendMediaType::File - || uploadingData.file->type == SendMediaType::ThemeFile - || uploadingData.file->type == SendMediaType::Audio) + if ((uploadingData.type() == SendMediaType::File + || uploadingData.type() == SendMediaType::ThemeFile + || uploadingData.type() == SendMediaType::Audio) && uploadingData.docSentParts <= kUseBigFilesFrom) { uploadingData.md5Hash.feed(toSend.constData(), toSend.size()); } @@ -502,7 +581,7 @@ void Uploader::sendNext() { mtpRequestId requestId; if (uploadingData.docSize > kUseBigFilesFrom) { requestId = _api->request(MTPupload_SaveBigFilePart( - MTP_long(uploadingData.file->id), + MTP_long(uploadingData.id()), MTP_int(uploadingData.docSentParts), MTP_int(uploadingData.docPartsCount), MTP_bytes(toSend) @@ -513,7 +592,7 @@ void Uploader::sendNext() { }).toDC(MTP::uploadDcId(todc)).send(); } else { requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(uploadingData.file->id), + MTP_long(uploadingData.id()), MTP_int(uploadingData.docSentParts), MTP_bytes(toSend) )).done([=](const MTPBool &result, mtpRequestId requestId) { @@ -644,18 +723,18 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } sentSize -= sentPartSize; sentSizes[dc] -= sentPartSize; - if (file.file->type == SendMediaType::Photo) { + if (file.type() == SendMediaType::Photo) { file.fileSentSize += sentPartSize; - const auto photo = session().data().photo(file.file->id); + const auto photo = session().data().photo(file.id()); if (photo->uploading() && file.file) { photo->uploadingData->size = file.file->partssize; photo->uploadingData->offset = file.fileSentSize; } _photoProgress.fire_copy(fullId); - } else if (file.file->type == SendMediaType::File - || file.file->type == SendMediaType::ThemeFile - || file.file->type == SendMediaType::Audio) { - const auto document = session().data().document(file.file->id); + } else if (file.type() == SendMediaType::File + || file.type() == SendMediaType::ThemeFile + || file.type() == SendMediaType::Audio) { + const auto document = session().data().document(file.id()); if (document->uploading()) { const auto doneParts = file.docSentParts - int(docRequestsSent.size()); @@ -664,7 +743,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { doneParts * file.docPartSize); } _documentProgress.fire_copy(fullId); - } else if (file.file->type == SendMediaType::Secure) { + } else if (file.type() == SendMediaType::Secure) { file.fileSentSize += sentPartSize; _secureProgress.fire_copy({ fullId, diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 4347e5c03..2e68307b9 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -12,7 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/facade.h" class ApiWrap; -struct FilePrepareResult; +struct FileLoadResult; +struct SendMediaReady; namespace Api { enum class SendProgressType; @@ -57,9 +58,10 @@ public: return uploadingId; } + void uploadMedia(const FullMsgId &msgId, const SendMediaReady &image); void upload( const FullMsgId &msgId, - const std::shared_ptr &file); + const std::shared_ptr &file); void cancel(const FullMsgId &msgId); void pause(const FullMsgId &msgId); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 192c7c112..c26b68458 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -222,6 +222,42 @@ int PhotoSideLimit() { return PhotoSideLimit(SendLargePhotos.value()); } +SendMediaReady::SendMediaReady( + SendMediaType type, + const QString &file, + const QString &filename, + int64 filesize, + const QByteArray &data, + const uint64 &id, + const uint64 &thumbId, + const QString &thumbExt, + const PeerId &peer, + const MTPPhoto &photo, + const PreparedPhotoThumbs &photoThumbs, + const MTPDocument &document, + const QByteArray &jpeg) +: type(type) +, file(file) +, filename(filename) +, filesize(filesize) +, data(data) +, thumbExt(thumbExt) +, id(id) +, thumbId(thumbId) +, peer(peer) +, photo(photo) +, document(document) +, photoThumbs(photoThumbs) { + if (!jpeg.isEmpty()) { + int32 size = jpeg.size(); + for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) { + parts.insert(part, jpeg.mid(i, kPhotoUploadPartSize)); + } + jpeg_md5.resize(32); + hashMd5Hex(jpeg.constData(), jpeg.size(), jpeg_md5.data()); + } +} + TaskQueue::TaskQueue(crl::time stopTimeoutMs) { if (stopTimeoutMs > 0) { _stopTimer = new QTimer(this); @@ -419,17 +455,22 @@ SendingAlbum::Item::Item(TaskId taskId) : taskId(taskId) { } -FilePrepareResult::FilePrepareResult(FilePrepareDescriptor &&descriptor) -: taskId(descriptor.taskId) -, id(descriptor.id) -, to(std::move(descriptor.to)) -, album(std::move(descriptor.album)) -, type(descriptor.type) -, caption(std::move(descriptor.caption)) -, spoiler(descriptor.spoiler) { +FileLoadResult::FileLoadResult( + TaskId taskId, + uint64 id, + const FileLoadTo &to, + const TextWithTags &caption, + bool spoiler, + std::shared_ptr album) +: taskId(taskId) +, id(id) +, to(to) +, album(std::move(album)) +, caption(caption) +, spoiler(spoiler) { } -void FilePrepareResult::setFileData(const QByteArray &filedata) { +void FileLoadResult::setFileData(const QByteArray &filedata) { if (filedata.isEmpty()) { partssize = 0; } else { @@ -442,7 +483,7 @@ void FilePrepareResult::setFileData(const QByteArray &filedata) { } } -void FilePrepareResult::setThumbData(const QByteArray &thumbdata) { +void FileLoadResult::setThumbData(const QByteArray &thumbdata) { if (!thumbdata.isEmpty()) { thumbbytes = thumbdata; int32 size = thumbdata.size(); @@ -454,11 +495,6 @@ void FilePrepareResult::setThumbData(const QByteArray &thumbdata) { } } -std::shared_ptr MakePreparedFile( - FilePrepareDescriptor &&descriptor) { - return std::make_shared(std::move(descriptor)); -} - FileLoadTask::FileLoadTask( not_null session, const QString &filepath, @@ -674,14 +710,13 @@ bool FileLoadTask::FillImageInformation( } void FileLoadTask::process(Args &&args) { - _result = MakePreparedFile({ - .taskId = id(), - .id = _id, - .to = _to, - .caption = _caption, - .spoiler = _spoiler, - .album = _album, - }); + _result = std::make_shared( + id(), + _id, + _to, + _caption, + _spoiler, + _album); QString filename, filemime; qint64 filesize = 0; @@ -1027,7 +1062,7 @@ void FileLoadTask::finish() { } } -FilePrepareResult *FileLoadTask::peekResult() const { +FileLoadResult *FileLoadTask::peekResult() const { return _result.get(); } diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 8b3b7bca7..d2e64b536 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -36,8 +36,43 @@ enum class SendMediaType { Secure, }; +using UploadFileParts = QMap; +struct SendMediaReady { + SendMediaReady() = default; // temp + SendMediaReady( + SendMediaType type, + const QString &file, + const QString &filename, + int64 filesize, + const QByteArray &data, + const uint64 &id, + const uint64 &thumbId, + const QString &thumbExt, + const PeerId &peer, + const MTPPhoto &photo, + const PreparedPhotoThumbs &photoThumbs, + const MTPDocument &document, + const QByteArray &jpeg); + + SendMediaType type; + QString file, filename; + int64 filesize = 0; + QByteArray data; + QString thumbExt; + uint64 id, thumbId; // id always file-id of media, thumbId is file-id of thumb ( == id for photos) + PeerId peer; + + MTPPhoto photo; + MTPDocument document; + PreparedPhotoThumbs photoThumbs; + UploadFileParts parts; + QByteArray jpeg_md5; + + QString caption; + +}; + using TaskId = void*; // no interface, just id -inline constexpr auto kEmptyTaskId = TaskId(); class Task { public: @@ -109,7 +144,7 @@ struct SendingAlbum { struct Item { explicit Item(TaskId taskId); - TaskId taskId = kEmptyTaskId; + TaskId taskId; uint64 randomId = 0; FullMsgId msgId; std::optional media; @@ -147,21 +182,17 @@ struct FileLoadTo { MsgId replaceMediaOf; }; -using UploadFileParts = QMap; -struct FilePrepareDescriptor { - TaskId taskId = kEmptyTaskId; - base::required id; - SendMediaType type = SendMediaType::File; - FileLoadTo to = { PeerId(), Api::SendOptions(), FullReplyTo(), MsgId() }; - TextWithTags caption; - bool spoiler = false; - std::shared_ptr album; -}; -struct FilePrepareResult { - explicit FilePrepareResult(FilePrepareDescriptor &&descriptor); +struct FileLoadResult { + FileLoadResult( + TaskId taskId, + uint64 id, + const FileLoadTo &to, + const TextWithTags &caption, + bool spoiler, + std::shared_ptr album); - TaskId taskId = kEmptyTaskId; - uint64 id = 0; + TaskId taskId; + uint64 id; FileLoadTo to; std::shared_ptr album; SendMediaType type = SendMediaType::File; @@ -185,8 +216,8 @@ struct FilePrepareResult { QImage goodThumbnail; QByteArray goodThumbnailBytes; - MTPPhoto photo = MTP_photoEmpty(MTP_long(0)); - MTPDocument document = MTP_documentEmpty(MTP_long(0)); + MTPPhoto photo; + MTPDocument document; PreparedPhotoThumbs photoThumbs; TextWithTags caption; @@ -199,9 +230,6 @@ struct FilePrepareResult { }; -[[nodiscard]] std::shared_ptr MakePreparedFile( - FilePrepareDescriptor &&descriptor); - class FileLoadTask final : public Task { public: static std::unique_ptr ReadMediaInformation( @@ -248,7 +276,7 @@ public: } void finish() override; - FilePrepareResult *peekResult() const; + FileLoadResult *peekResult() const; private: static bool CheckForSong( @@ -284,6 +312,6 @@ private: TextWithTags _caption; bool _spoiler = false; - std::shared_ptr _result; + std::shared_ptr _result; }; diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index b225b321e..7409a95ee 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -585,11 +585,11 @@ void ChatBackground::checkUploadWallPaper() { } const auto ready = PrepareWallPaper(_session->mainDcId(), _original); - const auto documentId = ready->id; + const auto documentId = ready.id; _wallPaperUploadId = FullMsgId( _session->userPeerId(), _session->data().nextLocalMessageId()); - _session->uploader().upload(_wallPaperUploadId, ready); + _session->uploader().uploadMedia(_wallPaperUploadId, ready); if (_wallPaperUploadLifetime) { return; } @@ -1529,9 +1529,7 @@ bool ReadPaletteValues(const QByteArray &content, Fn PrepareWallPaper( - MTP::DcId dcId, - const QImage &image) { +SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { PreparedPhotoThumbs thumbnails; QVector sizes; @@ -1557,7 +1555,6 @@ std::shared_ptr PrepareWallPaper( }; push("s", scaled(320)); - const auto id = base::RandomValue(); const auto filename = u"wallpaper.jpg"_q; auto attributes = QVector( 1, @@ -1565,20 +1562,8 @@ std::shared_ptr PrepareWallPaper( attributes.push_back(MTP_documentAttributeImageSize( MTP_int(image.width()), MTP_int(image.height()))); - - auto result = MakePreparedFile({ - .id = id, - .type = SendMediaType::ThemeFile, - }); - result->filename = filename; - result->content = jpeg; - result->filesize = jpeg.size(); - result->setFileData(jpeg); - if (thumbnails.empty()) { - result->thumb = thumbnails.front().second.image; - result->thumbbytes = thumbnails.front().second.bytes; - } - result->document = MTP_document( + const auto id = base::RandomValue(); + const auto document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -1590,7 +1575,21 @@ std::shared_ptr PrepareWallPaper( MTPVector(), MTP_int(dcId), MTP_vector(attributes)); - return result; + + return SendMediaReady( + SendMediaType::ThemeFile, + QString(), // filepath + filename, + jpeg.size(), + jpeg, + id, + 0, + QString(), + PeerId(), + MTP_photoEmpty(MTP_long(0)), + thumbnails, + document, + QByteArray()); } std::unique_ptr DefaultChatThemeOn(rpl::lifetime &lifetime) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index b0497fb0c..1425e4d07 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/style/style_core_palette.h" class QFileSystemWatcher; -struct FilePrepareResult; +struct SendMediaReady; namespace style { struct colorizer; @@ -298,7 +298,7 @@ private: }; -[[nodiscard]] std::shared_ptr PrepareWallPaper( +[[nodiscard]] SendMediaReady PrepareWallPaper( MTP::DcId dcId, const QImage &image); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index c430b3e28..d448ca185 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -385,7 +385,7 @@ bool CopyColorsToPalette( }); } -std::shared_ptr PrepareThemeMedia( +SendMediaReady PrepareThemeMedia( MTP::DcId dcId, const QString &name, const QByteArray &content) { @@ -403,29 +403,28 @@ std::shared_ptr PrepareThemeMedia( thumbnail.save(&buffer, "JPG", 87); } - sizes.push_back(MTP_photoSize( - MTP_string("s"), - MTP_int(thumbnail.width()), - MTP_int(thumbnail.height()), MTP_int(0))); + const auto push = [&]( + const char *type, + QImage &&image, + QByteArray bytes = QByteArray()) { + sizes.push_back(MTP_photoSize( + MTP_string(type), + MTP_int(image.width()), + MTP_int(image.height()), MTP_int(0))); + thumbnails.emplace(type[0], PreparedPhotoThumb{ + .image = std::move(image), + .bytes = std::move(bytes) + }); + }; + push("s", std::move(thumbnail), thumbnailBytes); - const auto id = base::RandomValue(); const auto filename = base::FileNameFromUserString(name) + u".tdesktop-theme"_q; auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - - auto result = MakePreparedFile({ - .id = id, - .type = SendMediaType::ThemeFile, - }); - result->filename = filename; - result->content = content; - result->filesize = content.size(); - result->thumb = thumbnail; - result->thumbname = "thumb.jpg"; - result->setThumbData(thumbnailBytes); - result->document = MTP_document( + const auto id = base::RandomValue(); + const auto document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -437,7 +436,21 @@ std::shared_ptr PrepareThemeMedia( MTPVector(), MTP_int(dcId), MTP_vector(attributes)); - return result; + + return SendMediaReady( + SendMediaType::ThemeFile, + QString(), // filepath + filename, + content.size(), + content, + id, + 0, + QString(), + PeerId(), + MTP_photoEmpty(MTP_long(0)), + thumbnails, + document, + thumbnailBytes); } Fn SavePreparedTheme( @@ -557,7 +570,7 @@ Fn SavePreparedTheme( session->mainDcId(), fields.title, theme); - state->filename = media->filename; + state->filename = media.filename; state->themeContent = theme; session->uploader().documentReady( @@ -567,7 +580,7 @@ Fn SavePreparedTheme( uploadTheme(data); }, state->lifetime); - session->uploader().upload(state->id, media); + session->uploader().uploadMedia(state->id, media); }; const auto save = [=] { @@ -986,15 +999,12 @@ ParsedTheme ParseTheme( [[nodiscard]] QString GenerateSlug() { const auto letters = uint8('Z' + 1 - 'A'); const auto digits = uint8('9' + 1 - '0'); - const auto firstValues = uint8(2 * letters); const auto values = uint8(2 * letters + digits); auto result = QString(); result.reserve(kRandomSlugSize); for (auto i = 0; i != kRandomSlugSize; ++i) { - const auto value = i - ? (base::RandomValue() % values) - : (base::RandomValue() % firstValues); + const auto value = base::RandomValue() % values; if (value < letters) { result.append(char('A' + value)); } else if (value < 2 * letters) { From ea178862d8ddf0da7671c14816abfee888869f74 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 20:41:13 +0400 Subject: [PATCH 33/71] Open some telegram.org and Telegram links in IV. --- .../SourceFiles/core/click_handler_types.h | 1 + Telegram/SourceFiles/core/ui_integration.cpp | 8 ++ .../inline_bots/bot_attach_web_view.cpp | 48 +------ .../inline_bots/bot_attach_web_view.h | 4 - Telegram/SourceFiles/iv/iv_instance.cpp | 124 ++++++++++++++++-- Telegram/SourceFiles/iv/iv_instance.h | 21 +++ 6 files changed, 146 insertions(+), 60 deletions(-) diff --git a/Telegram/SourceFiles/core/click_handler_types.h b/Telegram/SourceFiles/core/click_handler_types.h index 15a7dc1f9..e064d1e2d 100644 --- a/Telegram/SourceFiles/core/click_handler_types.h +++ b/Telegram/SourceFiles/core/click_handler_types.h @@ -46,6 +46,7 @@ struct ClickHandlerContext { bool mayShowConfirmation = false; bool skipBotAutoLogin = false; bool botStartAutoSubmit = false; + bool ignoreIv = false; // Is filled from peer info. PeerData *peer = nullptr; }; diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp index e6b62203d..6de0c7570 100644 --- a/Telegram/SourceFiles/core/ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/stickers/data_custom_emoji.h" #include "data/data_session.h" #include "data/data_sponsored_messages.h" +#include "iv/iv_instance.h" #include "ui/text/text_custom_emoji.h" #include "ui/basic_click_handlers.h" #include "ui/emoji_config.h" @@ -237,6 +238,13 @@ bool UiIntegration::handleUrlClick( } else if (local.startsWith(u"internal:"_q, Qt::CaseInsensitive)) { Core::App().openInternalUrl(local, context); return true; + } else if (Iv::PreferForUri(url) + && !context.value().ignoreIv) { + const auto my = context.value(); + if (const auto controller = my.sessionWindow.get()) { + Core::App().iv().openWithIvPreferred(controller, url, context); + return true; + } } auto parsed = UrlForAutoLogin(url); diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index c3f78b2d0..682afe3de 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -656,50 +656,12 @@ void AttachWebView::botHandleMenuButton(Ui::BotWebView::MenuButton button) { } void AttachWebView::botOpenIvLink(QString uri) { - const auto parts = uri.split('#'); - if (parts.isEmpty()) { - return; + const auto window = _context ? _context->controller.get() : nullptr; + if (window) { + Core::App().iv().openWithIvPreferred(window, uri); + } else { + Core::App().iv().openWithIvPreferred(_session, uri); } - const auto hash = (parts.size() > 1) ? parts[1] : u""_q; - const auto url = parts[0]; - if (const auto i = _ivCache.find(url); i != end(_ivCache)) { - const auto page = i->second; - if (page && page->iv) { - const auto window = _context - ? _context->controller.get() - : nullptr; - if (window) { - Core::App().iv().show(window, page->iv.get(), hash); - } else { - Core::App().iv().show(_session, page->iv.get(), hash); - } - } else { - UrlClickHandler::Open(uri); - } - return; - } else if (_ivRequestUri == uri) { - return; - } else if (_ivRequestId) { - _session->api().request(_ivRequestId).cancel(); - } - const auto finish = [=](WebPageData *page) { - _ivRequestId = 0; - _ivRequestUri = QString(); - _ivCache[url] = page; - botOpenIvLink(uri); - }; - _ivRequestUri = uri; - _ivRequestId = _session->api().request(MTPmessages_GetWebPage( - MTP_string(url), - MTP_int(0) - )).done([=](const MTPmessages_WebPage &result) { - const auto &data = result.data(); - _session->data().processUsers(data.vusers()); - _session->data().processChats(data.vchats()); - finish(_session->data().processWebpage(data.vwebpage())); - }).fail([=] { - finish(nullptr); - }).send(); } void AttachWebView::botSendData(QByteArray data) { diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h index c172cd5c3..aebf99332 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.h @@ -267,10 +267,6 @@ private: rpl::event_stream<> _attachBotsUpdates; base::flat_set> _disclaimerAccepted; - base::flat_map _ivCache; - QString _ivRequestUri; - mtpRequestId _ivRequestId = 0; - std::unique_ptr _panel; bool _catchingCancelInShowCall = false; diff --git a/Telegram/SourceFiles/iv/iv_instance.cpp b/Telegram/SourceFiles/iv/iv_instance.cpp index f84d55223..62527e35f 100644 --- a/Telegram/SourceFiles/iv/iv_instance.cpp +++ b/Telegram/SourceFiles/iv/iv_instance.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "core/file_utilities.h" #include "core/shortcuts.h" +#include "core/click_handler_types.h" #include "data/data_changes.h" #include "data/data_channel.h" #include "data/data_cloud_file.h" @@ -926,20 +927,104 @@ void Instance::show( } }, _shown->lifetime()); - if (!_tracking.contains(session)) { - _tracking.emplace(session); - session->lifetime().add([=] { - _tracking.remove(session); - _joining.remove(session); - _fullRequested.remove(session); - if (_shownSession == session) { - _shownSession = nullptr; - } - if (_shown && _shown->showingFrom(session)) { - _shown = nullptr; - } - }); + trackSession(session); +} + +void Instance::trackSession(not_null session) { + if (!_tracking.emplace(session).second) { + return; } + session->lifetime().add([=] { + _tracking.remove(session); + _joining.remove(session); + _fullRequested.remove(session); + _ivCache.remove(session); + if (_ivRequestSession == session) { + session->api().request(_ivRequestId).cancel(); + _ivRequestSession = nullptr; + _ivRequestUri = QString(); + _ivRequestId = 0; + } + if (_shownSession == session) { + _shownSession = nullptr; + } + if (_shown && _shown->showingFrom(session)) { + _shown = nullptr; + } + }); +} + +void Instance::openWithIvPreferred( + not_null controller, + QString uri, + QVariant context) { + auto my = context.value(); + my.sessionWindow = controller; + openWithIvPreferred( + &controller->session(), + uri, + QVariant::fromValue(my)); +} + +void Instance::openWithIvPreferred( + not_null session, + QString uri, + QVariant context) { + const auto openExternal = [=] { + auto my = context.value(); + my.ignoreIv = true; + UrlClickHandler::Open(uri, QVariant::fromValue(my)); + }; + const auto parts = uri.split('#'); + if (parts.isEmpty() || parts[0].isEmpty()) { + return; + } else if (!ShowButton()) { + return openExternal(); + } + trackSession(session); + const auto hash = (parts.size() > 1) ? parts[1] : u""_q; + const auto url = parts[0]; + auto &cache = _ivCache[session]; + if (const auto i = cache.find(url); i != end(cache)) { + const auto page = i->second; + if (page && page->iv) { + auto my = context.value(); + if (const auto window = my.sessionWindow.get()) { + show(window, page->iv.get(), hash); + } else { + show(session, page->iv.get(), hash); + } + } else { + openExternal(); + } + return; + } else if (_ivRequestSession == session.get() && _ivRequestUri == uri) { + return; + } else if (_ivRequestId) { + _ivRequestSession->api().request(_ivRequestId).cancel(); + } + const auto finish = [=](WebPageData *page) { + Expects(_ivRequestSession == session); + + _ivRequestId = 0; + _ivRequestUri = QString(); + _ivRequestSession = nullptr; + _ivCache[session][url] = page; + openWithIvPreferred(session, uri, context); + }; + _ivRequestSession = session; + _ivRequestUri = uri; + _ivRequestId = session->api().request(MTPmessages_GetWebPage( + MTP_string(url), + MTP_int(0) + )).done([=](const MTPmessages_WebPage &result) { + const auto &data = result.data(); + session->data().processUsers(data.vusers()); + session->data().processChats(data.vchats()); + finish(session->data().processWebpage(data.vwebpage())); + }).fail([=] { + finish(nullptr); + }).send(); } void Instance::requestFull( @@ -1033,4 +1118,17 @@ void Instance::closeAll() { _shown = nullptr; } +bool PreferForUri(const QString &uri) { + const auto url = QUrl(uri); + const auto host = url.host().toLower(); + const auto path = url.path().toLower(); + return (host == u"telegra.ph"_q) + || (host == u"te.legra.ph"_q) + || (host == u"graph.org"_q) + || (host == u"telegram.org"_q + && (path.startsWith(u"/faq"_q) + || path.startsWith(u"/privacy"_q) + || path.startsWith(u"/blog"_q))); +} + } // namespace Iv diff --git a/Telegram/SourceFiles/iv/iv_instance.h b/Telegram/SourceFiles/iv/iv_instance.h index 608c9c283..1299bbddd 100644 --- a/Telegram/SourceFiles/iv/iv_instance.h +++ b/Telegram/SourceFiles/iv/iv_instance.h @@ -41,6 +41,15 @@ public: not_null data, QString hash); + void openWithIvPreferred( + not_null controller, + QString uri, + QVariant context = {}); + void openWithIvPreferred( + not_null session, + QString uri, + QVariant context = {}); + [[nodiscard]] bool hasActiveWindow( not_null session) const; @@ -56,6 +65,8 @@ private: void processJoinChannel(const QString &context); void requestFull(not_null session, const QString &id); + void trackSession(not_null session); + const not_null _delegate; std::unique_ptr _shown; @@ -68,8 +79,18 @@ private: not_null, base::flat_set> _fullRequested; + base::flat_map< + not_null, + base::flat_map> _ivCache; + Main::Session *_ivRequestSession = nullptr; + QString _ivRequestUri; + mtpRequestId _ivRequestId = 0; + + rpl::lifetime _lifetime; }; +[[nodiscard]] bool PreferForUri(const QString &uri); + } // namespace Iv From 23883ed4a1db231d89fc678e5ef28c461a884945 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 21:08:56 +0400 Subject: [PATCH 34/71] Use generic way for FAQ links. --- Telegram/CMakeLists.txt | 2 - Telegram/SourceFiles/data/data_faq.cpp | 53 ------------------- Telegram/SourceFiles/data/data_faq.h | 27 ---------- Telegram/SourceFiles/data/data_session.h | 5 -- .../SourceFiles/settings/settings_intro.cpp | 2 +- .../SourceFiles/settings/settings_main.cpp | 13 ++++- Telegram/SourceFiles/settings/settings_main.h | 2 + Telegram/lib_base | 2 +- 8 files changed, 15 insertions(+), 91 deletions(-) delete mode 100644 Telegram/SourceFiles/data/data_faq.cpp delete mode 100644 Telegram/SourceFiles/data/data_faq.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 97337d1a4..4988f34b3 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -508,8 +508,6 @@ PRIVATE data/data_drafts.h data/data_emoji_statuses.cpp data/data_emoji_statuses.h - data/data_faq.cpp - data/data_faq.h data/data_folder.cpp data/data_folder.h data/data_forum.cpp diff --git a/Telegram/SourceFiles/data/data_faq.cpp b/Telegram/SourceFiles/data/data_faq.cpp deleted file mode 100644 index 97ef86459..000000000 --- a/Telegram/SourceFiles/data/data_faq.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#include "data/data_faq.h" - -#include "apiwrap.h" -#include "core/application.h" -#include "data/data_session.h" -#include "data/data_web_page.h" -#include "iv/iv_instance.h" -#include "lang/lang_keys.h" -#include "main/main_session.h" -#include "main/session/session_show.h" -#include "ui/basic_click_handlers.h" - -namespace Data { - -void Faq::open(std::shared_ptr show) { - if (!_id) { - show->session().api().request(MTPmessages_GetWebPage( - MTP_string(tr::lng_settings_faq_link(tr::now)), - MTP_int(0) - )).done([=](const MTPmessages_WebPage &result) { - show->session().data().processUsers(result.data().vusers()); - show->session().data().processChats(result.data().vchats()); - const auto page = show->session().data().processWebpage( - result.data().vwebpage()); - if (page && page->iv) { - _id = page->id; - open(show); - } else { - UrlClickHandler::Open(tr::lng_settings_faq_link(tr::now)); - } - }).fail([=] { - UrlClickHandler::Open(tr::lng_settings_faq_link(tr::now)); - }).send(); - } else { - const auto page = show->session().data().webpage(_id); - if (page && page->iv) { - const auto parts = tr::lng_settings_faq_link(tr::now).split('#'); - const auto hash = (parts.size() > 1) ? parts[1] : u""_q; - Core::App().iv().show(show, page->iv.get(), hash); - } else { - UrlClickHandler::Open(tr::lng_settings_faq_link(tr::now)); - } - } -} - -} // namespace Data diff --git a/Telegram/SourceFiles/data/data_faq.h b/Telegram/SourceFiles/data/data_faq.h deleted file mode 100644 index 7e0179c3b..000000000 --- a/Telegram/SourceFiles/data/data_faq.h +++ /dev/null @@ -1,27 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop application for the Telegram messaging service. - -For license and copyright information please follow this link: -https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL -*/ -#pragma once - -namespace Main { -class SessionShow; -} // namespace Main - -namespace Data { - -class Faq final { -public: - constexpr Faq() = default; - - void open(std::shared_ptr show); - -private: - WebPageId _id = 0; - -}; - -} // namespace Data diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 41aa27570..0f7df77f4 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "storage/storage_databases.h" #include "dialogs/dialogs_main_list.h" #include "data/data_groups.h" -#include "data/data_faq.h" #include "data/data_cloud_file.h" #include "history/history_location_manager.h" #include "base/timer.h" @@ -102,9 +101,6 @@ public: [[nodiscard]] const Groups &groups() const { return _groups; } - [[nodiscard]] Faq &faq() { - return _faq; - } [[nodiscard]] ChatFilters &chatsFilters() const { return *_chatsFilters; } @@ -1072,7 +1068,6 @@ private: mtpRequestId> _viewAsMessagesRequests; Groups _groups; - Faq _faq; const std::unique_ptr _chatsFilters; const std::unique_ptr _cloudThemes; const std::unique_ptr _sendActionManager; diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index 28665c913..6ddb33f75 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -110,7 +110,7 @@ object_ptr CreateIntroSettings( tr::lng_settings_faq(), st::settingsButtonNoIcon )->addClickHandler([] { - UrlClickHandler::Open(tr::lng_telegram_features_url(tr::now)); + OpenFaq(nullptr); }); return result; diff --git a/Telegram/SourceFiles/settings/settings_main.cpp b/Telegram/SourceFiles/settings/settings_main.cpp index 4b2469b71..a33485c11 100644 --- a/Telegram/SourceFiles/settings/settings_main.cpp +++ b/Telegram/SourceFiles/settings/settings_main.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_main.h" #include "core/application.h" +#include "core/click_handler_types.h" #include "settings/settings_business.h" #include "settings/settings_codes.h" #include "settings/settings_chat.h" @@ -613,7 +614,7 @@ void SetupHelp( st::settingsButton, { &st::menuIconFaq } )->addClickHandler([=] { - controller->session().data().faq().open(controller->uiShow()); + OpenFaq(controller); }); AddButtonWithIcon( @@ -659,7 +660,7 @@ void SetupHelp( .text = tr::lng_settings_ask_sure(), .confirmed = sure, .cancelled = [=](Fn close) { - controller->session().data().faq().open(controller->uiShow()); + OpenFaq(controller); close(); }, .confirmText = tr::lng_settings_ask_ok(), @@ -740,4 +741,12 @@ void Main::setupContent(not_null controller) { controller->session().data().cloudThemes().refresh(); } +void OpenFaq(base::weak_ptr weak) { + UrlClickHandler::Open( + tr::lng_settings_faq_link(tr::now), + QVariant::fromValue(ClickHandlerContext{ + .sessionWindow = weak, + })); +} + } // namespace Settings diff --git a/Telegram/SourceFiles/settings/settings_main.h b/Telegram/SourceFiles/settings/settings_main.h index 303c58c5d..a0f7e15ca 100644 --- a/Telegram/SourceFiles/settings/settings_main.h +++ b/Telegram/SourceFiles/settings/settings_main.h @@ -29,6 +29,8 @@ void SetupInterfaceScale( not_null container, bool icon = true); +void OpenFaq(base::weak_ptr weak); + class Main : public Section
{ public: Main(QWidget *parent, not_null controller); diff --git a/Telegram/lib_base b/Telegram/lib_base index 8d4b2efa0..beef65fac 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit 8d4b2efa0024016c6a6f6b4d8f1dfc3d62bafddc +Subproject commit beef65fac578a812dade36557f4c666ac3d3ba78 From 690d52f7bc8059cf06cd45a1b9e57f4b94f192c4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 21:13:00 +0400 Subject: [PATCH 35/71] Version 4.16.2. - Use IV by default for Telegraph and some Telegram links. - Support IV links in bot web-apps. - Some bug fixes. --- 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, 21 insertions(+), 15 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index d97818b8e..821e6eb70 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.16.2.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 7e6c0165e..d2f50744e 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,1,0 - PRODUCTVERSION 4,16,1,0 + FILEVERSION 4,16,2,0 + PRODUCTVERSION 4,16,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.16.1.0" + VALUE "FileVersion", "4.16.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.1.0" + VALUE "ProductVersion", "4.16.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 4b7bc1131..a395fa632 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,1,0 - PRODUCTVERSION 4,16,1,0 + FILEVERSION 4,16,2,0 + PRODUCTVERSION 4,16,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "4.16.1.0" + VALUE "FileVersion", "4.16.2.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.1.0" + VALUE "ProductVersion", "4.16.2.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 027295e4f..beb3cc33b 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 = 4016001; -constexpr auto AppVersionStr = "4.16.1"; +constexpr auto AppVersion = 4016002; +constexpr auto AppVersionStr = "4.16.2"; constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 91212612f..20d886536 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4016001 +AppVersion 4016002 AppVersionStrMajor 4.16 -AppVersionStrSmall 4.16.1 -AppVersionStr 4.16.1 +AppVersionStrSmall 4.16.2 +AppVersionStr 4.16.2 BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 4.16.1 +AppVersionOriginal 4.16.2 diff --git a/changelog.txt b/changelog.txt index 56ff2649b..a0808da30 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +4.16.2 (04.04.24) + +- Use IV by default for Telegraph and some Telegram links. +- Support IV links in bot web-apps. +- Some bug fixes. + 4.16.1 (02.04.24) - Show information about Fragment phone numbers and usernames. From b39e78a4a9bb559f3595fee41e0c8d2137f7d1ef Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 18:36:48 +0400 Subject: [PATCH 36/71] Reapply "Remove SendMediaReady legacy helper." This reverts commit 09f07a7a9dde2c2b58c5a29fa6c20a95f4522dea. --- Telegram/SourceFiles/api/api_peer_photo.cpp | 33 ++-- Telegram/SourceFiles/api/api_ringtones.cpp | 33 ++-- Telegram/SourceFiles/api/api_sending.cpp | 2 +- Telegram/SourceFiles/api/api_sending.h | 4 +- .../boxes/background_preview_box.cpp | 4 +- Telegram/SourceFiles/history/history_widget.h | 1 - Telegram/SourceFiles/iv/iv_data.cpp | 2 +- Telegram/SourceFiles/mainwidget.h | 1 - .../passport/passport_form_controller.cpp | 12 +- Telegram/SourceFiles/storage/file_upload.cpp | 177 +++++------------- Telegram/SourceFiles/storage/file_upload.h | 6 +- .../SourceFiles/storage/localimageloader.cpp | 83 +++----- .../SourceFiles/storage/localimageloader.h | 74 +++----- .../window/themes/window_theme.cpp | 41 ++-- .../SourceFiles/window/themes/window_theme.h | 4 +- .../window/themes/window_theme_editor_box.cpp | 60 +++--- 16 files changed, 183 insertions(+), 354 deletions(-) diff --git a/Telegram/SourceFiles/api/api_peer_photo.cpp b/Telegram/SourceFiles/api/api_peer_photo.cpp index c70ef4491..9c8a470fa 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.cpp +++ b/Telegram/SourceFiles/api/api_peer_photo.cpp @@ -34,7 +34,7 @@ namespace { constexpr auto kSharedMediaLimit = 100; -[[nodiscard]] SendMediaReady PreparePeerPhoto( +[[nodiscard]] std::shared_ptr PreparePeerPhoto( MTP::DcId dcId, PeerId peerId, QImage &&image) { @@ -80,24 +80,17 @@ constexpr auto kSharedMediaLimit = 100; MTPVector(), MTP_int(dcId)); - QString file, filename; - int64 filesize = 0; - QByteArray data; - - return SendMediaReady( - SendMediaType::Photo, - file, - filename, - filesize, - data, - id, - id, - u"jpg"_q, - peerId, - photo, - photoThumbs, - MTP_documentEmpty(MTP_long(0)), - jpeg); + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::Photo, + }); + result->type = SendMediaType::Photo; + result->setFileData(jpeg); + result->thumbId = id; + result->thumbname = "thumb.jpg"; + result->photo = photo; + result->photoThumbs = photoThumbs; + return result; } [[nodiscard]] std::optional PrepareMtpMarkup( @@ -239,7 +232,7 @@ void PeerPhoto::upload( _api.instance().mainDcId(), peer->id, base::take(photo.image)); - _session->uploader().uploadMedia(fakeId, ready); + _session->uploader().upload(fakeId, ready); } } diff --git a/Telegram/SourceFiles/api/api_ringtones.cpp b/Telegram/SourceFiles/api/api_ringtones.cpp index 87522bf58..307ba580a 100644 --- a/Telegram/SourceFiles/api/api_ringtones.cpp +++ b/Telegram/SourceFiles/api/api_ringtones.cpp @@ -24,16 +24,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Api { namespace { -SendMediaReady PrepareRingtoneDocument( +std::shared_ptr PrepareRingtoneDocument( MTP::DcId dcId, const QString &filename, const QString &filemime, const QByteArray &content) { + const auto id = base::RandomValue(); auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - const auto id = base::RandomValue(); - const auto document = MTP_document( + + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::File, + }); + result->filename = filename; + result->content = content; + result->filesize = content.size(); + result->setFileData(content); + result->document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -45,21 +54,7 @@ SendMediaReady PrepareRingtoneDocument( MTPVector(), MTP_int(dcId), MTP_vector(std::move(attributes))); - - return SendMediaReady( - SendMediaType::File, - QString(), // filepath - filename, - content.size(), - content, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - PreparedPhotoThumbs(), - document, - QByteArray()); + return result; } } // namespace @@ -102,7 +97,7 @@ void Ringtones::upload( _uploads.erase(already); } _uploads.emplace(fakeId, uploadedData); - _session->uploader().uploadMedia(fakeId, ready); + _session->uploader().upload(fakeId, ready); } void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) { diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 72bd06b3d..d91943fe0 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -353,7 +353,7 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file) { + const std::shared_ptr &file) { const auto isEditing = (file->type != SendMediaType::Audio) && (file->to.replaceMediaOf != 0); const auto newId = FullMsgId( diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h index e17c66f3e..2fdbad843 100644 --- a/Telegram/SourceFiles/api/api_sending.h +++ b/Telegram/SourceFiles/api/api_sending.h @@ -14,7 +14,7 @@ class Session; class History; class PhotoData; class DocumentData; -struct FileLoadResult; +struct FilePrepareResult; namespace Api { @@ -40,6 +40,6 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file); + const std::shared_ptr &file); } // namespace Api diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index 3a9917719..a112c478a 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -593,11 +593,11 @@ void BackgroundPreviewBox::uploadForPeer(bool both) { const auto ready = Window::Theme::PrepareWallPaper( session->mainDcId(), _paper.localThumbnail()->original()); - const auto documentId = ready.id; + const auto documentId = ready->id; _uploadId = FullMsgId( session->userPeerId(), session->data().nextLocalMessageId()); - session->uploader().uploadMedia(_uploadId, ready); + session->uploader().upload(_uploadId, ready); if (_uploadLifetime) { return; } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 43966331e..b4d8bb49c 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/fields/input_field.h" #include "mtproto/sender.h" -struct FileLoadResult; enum class SendMediaType; class MessageLinksParser; struct InlineBotQuery; diff --git a/Telegram/SourceFiles/iv/iv_data.cpp b/Telegram/SourceFiles/iv/iv_data.cpp index bbcca0b2c..5258e6c9f 100644 --- a/Telegram/SourceFiles/iv/iv_data.cpp +++ b/Telegram/SourceFiles/iv/iv_data.cpp @@ -25,7 +25,7 @@ QByteArray GeoPointId(Geo point) { const auto lon = int(point.lon * 1000000); const auto combined = (std::uint64_t(std::uint32_t(lat)) << 32) | std::uint64_t(std::uint32_t(lon)); - return QByteArray::number(combined) + return QByteArray::number(quint64(combined)) + ',' + QByteArray::number(point.access); } diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 10bea63d4..809086765 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -16,7 +16,6 @@ struct HistoryMessageMarkupButton; class MainWindow; class HistoryWidget; class StackItem; -struct FileLoadResult; class History; class Image; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 684163163..980888ffb 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -1581,14 +1581,10 @@ void FormController::uploadEncryptedFile( &session(), std::make_unique(std::move(data))); - auto prepared = std::make_shared( - TaskId(), - file.uploadData->fileId, - FileLoadTo(PeerId(), Api::SendOptions(), FullReplyTo(), MsgId()), - TextWithTags(), - false, - std::shared_ptr(nullptr)); - prepared->type = SendMediaType::Secure; + auto prepared = MakePreparedFile({ + .id = file.uploadData->fileId, + .type = SendMediaType::Secure, + }); prepared->content = QByteArray::fromRawData( reinterpret_cast(file.uploadData->bytes.data()), file.uploadData->bytes.size()); diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index c40396955..e0c7f8b57 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -59,22 +59,15 @@ constexpr auto kKillSessionTimeout = 15 * crl::time(1000); } // namespace struct Uploader::File { - File(const SendMediaReady &media); - File(const std::shared_ptr &file); + explicit File(const std::shared_ptr &file); void setDocSize(int64 size); bool setPartSize(uint32 partSize); - std::shared_ptr file; - SendMediaReady media; + std::shared_ptr file; int32 partsCount = 0; mutable int64 fileSentSize = 0; - uint64 id() const; - SendMediaType type() const; - uint64 thumbId() const; - const QString &filename() const; - HashMd5 md5Hash; std::unique_ptr docFile; @@ -85,27 +78,15 @@ struct Uploader::File { }; -Uploader::File::File(const SendMediaReady &media) : media(media) { - partsCount = media.parts.size(); - if (type() == SendMediaType::File - || type() == SendMediaType::ThemeFile - || type() == SendMediaType::Audio) { - setDocSize(media.file.isEmpty() - ? media.data.size() - : media.filesize); - } else { - docSize = docPartSize = docPartsCount = 0; - } -} -Uploader::File::File(const std::shared_ptr &file) +Uploader::File::File(const std::shared_ptr &file) : file(file) { - partsCount = (type() == SendMediaType::Photo - || type() == SendMediaType::Secure) + partsCount = (file->type == SendMediaType::Photo + || file->type == SendMediaType::Secure) ? file->fileparts.size() : file->thumbparts.size(); - if (type() == SendMediaType::File - || type() == SendMediaType::ThemeFile - || type() == SendMediaType::Audio) { + if (file->type == SendMediaType::File + || file->type == SendMediaType::ThemeFile + || file->type == SendMediaType::Audio) { setDocSize(file->filesize); } else { docSize = docPartSize = docPartsCount = 0; @@ -134,22 +115,6 @@ bool Uploader::File::setPartSize(uint32 partSize) { return (docPartsCount <= kDocumentMaxPartsCountDefault); } -uint64 Uploader::File::id() const { - return file ? file->id : media.id; -} - -SendMediaType Uploader::File::type() const { - return file ? file->type : media.type; -} - -uint64 Uploader::File::thumbId() const { - return file ? file->thumbId : media.thumbId; -} - -const QString &Uploader::File::filename() const { - return file ? file->filename : media.filename; -} - Uploader::Uploader(not_null api) : _api(api) , _nextTimer([=] { sendNext(); }) @@ -282,39 +247,9 @@ Main::Session &Uploader::session() const { return _api->session(); } -void Uploader::uploadMedia( - const FullMsgId &msgId, - const SendMediaReady &media) { - if (media.type == SendMediaType::Photo) { - session().data().processPhoto(media.photo, media.photoThumbs); - } else if (media.type == SendMediaType::File - || media.type == SendMediaType::ThemeFile - || media.type == SendMediaType::Audio) { - const auto document = media.photoThumbs.empty() - ? session().data().processDocument(media.document) - : session().data().processDocument( - media.document, - Images::FromImageInMemory( - media.photoThumbs.front().second.image, - "JPG", - media.photoThumbs.front().second.bytes)); - if (!media.data.isEmpty()) { - document->setDataAndCache(media.data); - if (media.type == SendMediaType::ThemeFile) { - document->checkWallPaperProperties(); - } - } - if (!media.file.isEmpty()) { - document->setLocation(Core::FileLocation(media.file)); - } - } - queue.emplace(msgId, File(media)); - sendNext(); -} - void Uploader::upload( const FullMsgId &msgId, - const std::shared_ptr &file) { + const std::shared_ptr &file) { if (file->type == SendMediaType::Photo) { const auto photo = session().data().processPhoto( file->photo, @@ -383,13 +318,13 @@ void Uploader::currentFailed() { } void Uploader::notifyFailed(FullMsgId id, const File &file) { - const auto type = file.type(); + const auto type = file.file->type; if (type == SendMediaType::Photo) { _photoFailed.fire_copy(id); } else if (type == SendMediaType::File || type == SendMediaType::ThemeFile || type == SendMediaType::Audio) { - const auto document = session().data().document(file.id()); + const auto document = session().data().document(file.file->id); if (document->uploading()) { document->status = FileUploadFailed; } @@ -439,18 +374,14 @@ void Uploader::sendNext() { } } - auto &parts = uploadingData.file - ? ((uploadingData.type() == SendMediaType::Photo - || uploadingData.type() == SendMediaType::Secure) - ? uploadingData.file->fileparts - : uploadingData.file->thumbparts) - : uploadingData.media.parts; - const auto partsOfId = uploadingData.file - ? ((uploadingData.type() == SendMediaType::Photo - || uploadingData.type() == SendMediaType::Secure) - ? uploadingData.file->id - : uploadingData.file->thumbId) - : uploadingData.media.thumbId; + auto &parts = (uploadingData.file->type == SendMediaType::Photo + || uploadingData.file->type == SendMediaType::Secure) + ? uploadingData.file->fileparts + : uploadingData.file->thumbparts; + const auto partsOfId = (uploadingData.file->type == SendMediaType::Photo + || uploadingData.file->type == SendMediaType::Secure) + ? uploadingData.file->id + : uploadingData.file->thumbId; if (parts.isEmpty()) { if (uploadingData.docSentParts >= uploadingData.docPartsCount) { if (requestsSent.empty() && docRequestsSent.empty()) { @@ -462,19 +393,17 @@ void Uploader::sendNext() { const auto attachedStickers = uploadingData.file ? uploadingData.file->attachedStickers : std::vector(); - if (uploadingData.type() == SendMediaType::Photo) { - auto photoFilename = uploadingData.filename(); + if (uploadingData.file->type == SendMediaType::Photo) { + auto photoFilename = uploadingData.file->filename; if (!photoFilename.endsWith(u".jpg"_q, Qt::CaseInsensitive)) { // Server has some extensions checking for inputMediaUploadedPhoto, // so force the extension to be .jpg anyway. It doesn't matter, // because the filename from inputFile is not used anywhere. photoFilename += u".jpg"_q; } - const auto md5 = uploadingData.file - ? uploadingData.file->filemd5 - : uploadingData.media.jpeg_md5; + const auto md5 = uploadingData.file->filemd5; const auto file = MTP_inputFile( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.partsCount), MTP_string(photoFilename), MTP_bytes(md5)); @@ -487,34 +416,30 @@ void Uploader::sendNext() { .options = options, .edit = edit, }); - } else if (uploadingData.type() == SendMediaType::File - || uploadingData.type() == SendMediaType::ThemeFile - || uploadingData.type() == SendMediaType::Audio) { + } else if (uploadingData.file->type == SendMediaType::File + || uploadingData.file->type == SendMediaType::ThemeFile + || uploadingData.file->type == SendMediaType::Audio) { QByteArray docMd5(32, Qt::Uninitialized); hashMd5Hex(uploadingData.md5Hash.result(), docMd5.data()); const auto file = (uploadingData.docSize > kUseBigFilesFrom) ? MTP_inputFileBig( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.filename())) + MTP_string(uploadingData.file->filename)) : MTP_inputFile( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.filename()), + MTP_string(uploadingData.file->filename), MTP_bytes(docMd5)); const auto thumb = [&]() -> std::optional { if (!uploadingData.partsCount) { return std::nullopt; } - const auto thumbFilename = uploadingData.file - ? uploadingData.file->thumbname - : (u"thumb."_q + uploadingData.media.thumbExt); - const auto thumbMd5 = uploadingData.file - ? uploadingData.file->thumbmd5 - : uploadingData.media.jpeg_md5; + const auto thumbFilename = uploadingData.file->thumbname; + const auto thumbMd5 = uploadingData.file->thumbmd5; return MTP_inputFile( - MTP_long(uploadingData.thumbId()), + MTP_long(uploadingData.file->thumbId), MTP_int(uploadingData.partsCount), MTP_string(thumbFilename), MTP_bytes(thumbMd5)); @@ -529,10 +454,10 @@ void Uploader::sendNext() { .options = options, .edit = edit, }); - } else if (uploadingData.type() == SendMediaType::Secure) { + } else if (uploadingData.file->type == SendMediaType::Secure) { _secureReady.fire({ uploadingId, - uploadingData.id(), + uploadingData.file->id, uploadingData.partsCount }); } queue.erase(uploadingId); @@ -542,15 +467,11 @@ void Uploader::sendNext() { return; } - auto &content = uploadingData.file - ? uploadingData.file->content - : uploadingData.media.data; + auto &content = uploadingData.file->content; QByteArray toSend; if (content.isEmpty()) { if (!uploadingData.docFile) { - const auto filepath = uploadingData.file - ? uploadingData.file->filepath - : uploadingData.media.file; + const auto filepath = uploadingData.file->filepath; uploadingData.docFile = std::make_unique(filepath); if (!uploadingData.docFile->open(QIODevice::ReadOnly)) { currentFailed(); @@ -565,9 +486,9 @@ void Uploader::sendNext() { const auto offset = uploadingData.docSentParts * uploadingData.docPartSize; toSend = content.mid(offset, uploadingData.docPartSize); - if ((uploadingData.type() == SendMediaType::File - || uploadingData.type() == SendMediaType::ThemeFile - || uploadingData.type() == SendMediaType::Audio) + if ((uploadingData.file->type == SendMediaType::File + || uploadingData.file->type == SendMediaType::ThemeFile + || uploadingData.file->type == SendMediaType::Audio) && uploadingData.docSentParts <= kUseBigFilesFrom) { uploadingData.md5Hash.feed(toSend.constData(), toSend.size()); } @@ -581,7 +502,7 @@ void Uploader::sendNext() { mtpRequestId requestId; if (uploadingData.docSize > kUseBigFilesFrom) { requestId = _api->request(MTPupload_SaveBigFilePart( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docSentParts), MTP_int(uploadingData.docPartsCount), MTP_bytes(toSend) @@ -592,7 +513,7 @@ void Uploader::sendNext() { }).toDC(MTP::uploadDcId(todc)).send(); } else { requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(uploadingData.id()), + MTP_long(uploadingData.file->id), MTP_int(uploadingData.docSentParts), MTP_bytes(toSend) )).done([=](const MTPBool &result, mtpRequestId requestId) { @@ -723,18 +644,18 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } sentSize -= sentPartSize; sentSizes[dc] -= sentPartSize; - if (file.type() == SendMediaType::Photo) { + if (file.file->type == SendMediaType::Photo) { file.fileSentSize += sentPartSize; - const auto photo = session().data().photo(file.id()); + const auto photo = session().data().photo(file.file->id); if (photo->uploading() && file.file) { photo->uploadingData->size = file.file->partssize; photo->uploadingData->offset = file.fileSentSize; } _photoProgress.fire_copy(fullId); - } else if (file.type() == SendMediaType::File - || file.type() == SendMediaType::ThemeFile - || file.type() == SendMediaType::Audio) { - const auto document = session().data().document(file.id()); + } else if (file.file->type == SendMediaType::File + || file.file->type == SendMediaType::ThemeFile + || file.file->type == SendMediaType::Audio) { + const auto document = session().data().document(file.file->id); if (document->uploading()) { const auto doneParts = file.docSentParts - int(docRequestsSent.size()); @@ -743,7 +664,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { doneParts * file.docPartSize); } _documentProgress.fire_copy(fullId); - } else if (file.type() == SendMediaType::Secure) { + } else if (file.file->type == SendMediaType::Secure) { file.fileSentSize += sentPartSize; _secureProgress.fire_copy({ fullId, diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 2e68307b9..4347e5c03 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -12,8 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/facade.h" class ApiWrap; -struct FileLoadResult; -struct SendMediaReady; +struct FilePrepareResult; namespace Api { enum class SendProgressType; @@ -58,10 +57,9 @@ public: return uploadingId; } - void uploadMedia(const FullMsgId &msgId, const SendMediaReady &image); void upload( const FullMsgId &msgId, - const std::shared_ptr &file); + const std::shared_ptr &file); void cancel(const FullMsgId &msgId); void pause(const FullMsgId &msgId); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index c26b68458..192c7c112 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -222,42 +222,6 @@ int PhotoSideLimit() { return PhotoSideLimit(SendLargePhotos.value()); } -SendMediaReady::SendMediaReady( - SendMediaType type, - const QString &file, - const QString &filename, - int64 filesize, - const QByteArray &data, - const uint64 &id, - const uint64 &thumbId, - const QString &thumbExt, - const PeerId &peer, - const MTPPhoto &photo, - const PreparedPhotoThumbs &photoThumbs, - const MTPDocument &document, - const QByteArray &jpeg) -: type(type) -, file(file) -, filename(filename) -, filesize(filesize) -, data(data) -, thumbExt(thumbExt) -, id(id) -, thumbId(thumbId) -, peer(peer) -, photo(photo) -, document(document) -, photoThumbs(photoThumbs) { - if (!jpeg.isEmpty()) { - int32 size = jpeg.size(); - for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) { - parts.insert(part, jpeg.mid(i, kPhotoUploadPartSize)); - } - jpeg_md5.resize(32); - hashMd5Hex(jpeg.constData(), jpeg.size(), jpeg_md5.data()); - } -} - TaskQueue::TaskQueue(crl::time stopTimeoutMs) { if (stopTimeoutMs > 0) { _stopTimer = new QTimer(this); @@ -455,22 +419,17 @@ SendingAlbum::Item::Item(TaskId taskId) : taskId(taskId) { } -FileLoadResult::FileLoadResult( - TaskId taskId, - uint64 id, - const FileLoadTo &to, - const TextWithTags &caption, - bool spoiler, - std::shared_ptr album) -: taskId(taskId) -, id(id) -, to(to) -, album(std::move(album)) -, caption(caption) -, spoiler(spoiler) { +FilePrepareResult::FilePrepareResult(FilePrepareDescriptor &&descriptor) +: taskId(descriptor.taskId) +, id(descriptor.id) +, to(std::move(descriptor.to)) +, album(std::move(descriptor.album)) +, type(descriptor.type) +, caption(std::move(descriptor.caption)) +, spoiler(descriptor.spoiler) { } -void FileLoadResult::setFileData(const QByteArray &filedata) { +void FilePrepareResult::setFileData(const QByteArray &filedata) { if (filedata.isEmpty()) { partssize = 0; } else { @@ -483,7 +442,7 @@ void FileLoadResult::setFileData(const QByteArray &filedata) { } } -void FileLoadResult::setThumbData(const QByteArray &thumbdata) { +void FilePrepareResult::setThumbData(const QByteArray &thumbdata) { if (!thumbdata.isEmpty()) { thumbbytes = thumbdata; int32 size = thumbdata.size(); @@ -495,6 +454,11 @@ void FileLoadResult::setThumbData(const QByteArray &thumbdata) { } } +std::shared_ptr MakePreparedFile( + FilePrepareDescriptor &&descriptor) { + return std::make_shared(std::move(descriptor)); +} + FileLoadTask::FileLoadTask( not_null session, const QString &filepath, @@ -710,13 +674,14 @@ bool FileLoadTask::FillImageInformation( } void FileLoadTask::process(Args &&args) { - _result = std::make_shared( - id(), - _id, - _to, - _caption, - _spoiler, - _album); + _result = MakePreparedFile({ + .taskId = id(), + .id = _id, + .to = _to, + .caption = _caption, + .spoiler = _spoiler, + .album = _album, + }); QString filename, filemime; qint64 filesize = 0; @@ -1062,7 +1027,7 @@ void FileLoadTask::finish() { } } -FileLoadResult *FileLoadTask::peekResult() const { +FilePrepareResult *FileLoadTask::peekResult() const { return _result.get(); } diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index d2e64b536..8b3b7bca7 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -36,43 +36,8 @@ enum class SendMediaType { Secure, }; -using UploadFileParts = QMap; -struct SendMediaReady { - SendMediaReady() = default; // temp - SendMediaReady( - SendMediaType type, - const QString &file, - const QString &filename, - int64 filesize, - const QByteArray &data, - const uint64 &id, - const uint64 &thumbId, - const QString &thumbExt, - const PeerId &peer, - const MTPPhoto &photo, - const PreparedPhotoThumbs &photoThumbs, - const MTPDocument &document, - const QByteArray &jpeg); - - SendMediaType type; - QString file, filename; - int64 filesize = 0; - QByteArray data; - QString thumbExt; - uint64 id, thumbId; // id always file-id of media, thumbId is file-id of thumb ( == id for photos) - PeerId peer; - - MTPPhoto photo; - MTPDocument document; - PreparedPhotoThumbs photoThumbs; - UploadFileParts parts; - QByteArray jpeg_md5; - - QString caption; - -}; - using TaskId = void*; // no interface, just id +inline constexpr auto kEmptyTaskId = TaskId(); class Task { public: @@ -144,7 +109,7 @@ struct SendingAlbum { struct Item { explicit Item(TaskId taskId); - TaskId taskId; + TaskId taskId = kEmptyTaskId; uint64 randomId = 0; FullMsgId msgId; std::optional media; @@ -182,17 +147,21 @@ struct FileLoadTo { MsgId replaceMediaOf; }; -struct FileLoadResult { - FileLoadResult( - TaskId taskId, - uint64 id, - const FileLoadTo &to, - const TextWithTags &caption, - bool spoiler, - std::shared_ptr album); +using UploadFileParts = QMap; +struct FilePrepareDescriptor { + TaskId taskId = kEmptyTaskId; + base::required id; + SendMediaType type = SendMediaType::File; + FileLoadTo to = { PeerId(), Api::SendOptions(), FullReplyTo(), MsgId() }; + TextWithTags caption; + bool spoiler = false; + std::shared_ptr album; +}; +struct FilePrepareResult { + explicit FilePrepareResult(FilePrepareDescriptor &&descriptor); - TaskId taskId; - uint64 id; + TaskId taskId = kEmptyTaskId; + uint64 id = 0; FileLoadTo to; std::shared_ptr album; SendMediaType type = SendMediaType::File; @@ -216,8 +185,8 @@ struct FileLoadResult { QImage goodThumbnail; QByteArray goodThumbnailBytes; - MTPPhoto photo; - MTPDocument document; + MTPPhoto photo = MTP_photoEmpty(MTP_long(0)); + MTPDocument document = MTP_documentEmpty(MTP_long(0)); PreparedPhotoThumbs photoThumbs; TextWithTags caption; @@ -230,6 +199,9 @@ struct FileLoadResult { }; +[[nodiscard]] std::shared_ptr MakePreparedFile( + FilePrepareDescriptor &&descriptor); + class FileLoadTask final : public Task { public: static std::unique_ptr ReadMediaInformation( @@ -276,7 +248,7 @@ public: } void finish() override; - FileLoadResult *peekResult() const; + FilePrepareResult *peekResult() const; private: static bool CheckForSong( @@ -312,6 +284,6 @@ private: TextWithTags _caption; bool _spoiler = false; - std::shared_ptr _result; + std::shared_ptr _result; }; diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 7409a95ee..b225b321e 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -585,11 +585,11 @@ void ChatBackground::checkUploadWallPaper() { } const auto ready = PrepareWallPaper(_session->mainDcId(), _original); - const auto documentId = ready.id; + const auto documentId = ready->id; _wallPaperUploadId = FullMsgId( _session->userPeerId(), _session->data().nextLocalMessageId()); - _session->uploader().uploadMedia(_wallPaperUploadId, ready); + _session->uploader().upload(_wallPaperUploadId, ready); if (_wallPaperUploadLifetime) { return; } @@ -1529,7 +1529,9 @@ bool ReadPaletteValues(const QByteArray &content, Fn PrepareWallPaper( + MTP::DcId dcId, + const QImage &image) { PreparedPhotoThumbs thumbnails; QVector sizes; @@ -1555,6 +1557,7 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { }; push("s", scaled(320)); + const auto id = base::RandomValue(); const auto filename = u"wallpaper.jpg"_q; auto attributes = QVector( 1, @@ -1562,8 +1565,20 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { attributes.push_back(MTP_documentAttributeImageSize( MTP_int(image.width()), MTP_int(image.height()))); - const auto id = base::RandomValue(); - const auto document = MTP_document( + + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::ThemeFile, + }); + result->filename = filename; + result->content = jpeg; + result->filesize = jpeg.size(); + result->setFileData(jpeg); + if (thumbnails.empty()) { + result->thumb = thumbnails.front().second.image; + result->thumbbytes = thumbnails.front().second.bytes; + } + result->document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -1575,21 +1590,7 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) { MTPVector(), MTP_int(dcId), MTP_vector(attributes)); - - return SendMediaReady( - SendMediaType::ThemeFile, - QString(), // filepath - filename, - jpeg.size(), - jpeg, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - thumbnails, - document, - QByteArray()); + return result; } std::unique_ptr DefaultChatThemeOn(rpl::lifetime &lifetime) { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index 1425e4d07..b0497fb0c 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/style/style_core_palette.h" class QFileSystemWatcher; -struct SendMediaReady; +struct FilePrepareResult; namespace style { struct colorizer; @@ -298,7 +298,7 @@ private: }; -[[nodiscard]] SendMediaReady PrepareWallPaper( +[[nodiscard]] std::shared_ptr PrepareWallPaper( MTP::DcId dcId, const QImage &image); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index d448ca185..c430b3e28 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -385,7 +385,7 @@ bool CopyColorsToPalette( }); } -SendMediaReady PrepareThemeMedia( +std::shared_ptr PrepareThemeMedia( MTP::DcId dcId, const QString &name, const QByteArray &content) { @@ -403,28 +403,29 @@ SendMediaReady PrepareThemeMedia( thumbnail.save(&buffer, "JPG", 87); } - const auto push = [&]( - const char *type, - QImage &&image, - QByteArray bytes = QByteArray()) { - sizes.push_back(MTP_photoSize( - MTP_string(type), - MTP_int(image.width()), - MTP_int(image.height()), MTP_int(0))); - thumbnails.emplace(type[0], PreparedPhotoThumb{ - .image = std::move(image), - .bytes = std::move(bytes) - }); - }; - push("s", std::move(thumbnail), thumbnailBytes); + sizes.push_back(MTP_photoSize( + MTP_string("s"), + MTP_int(thumbnail.width()), + MTP_int(thumbnail.height()), MTP_int(0))); + const auto id = base::RandomValue(); const auto filename = base::FileNameFromUserString(name) + u".tdesktop-theme"_q; auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - const auto id = base::RandomValue(); - const auto document = MTP_document( + + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::ThemeFile, + }); + result->filename = filename; + result->content = content; + result->filesize = content.size(); + result->thumb = thumbnail; + result->thumbname = "thumb.jpg"; + result->setThumbData(thumbnailBytes); + result->document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -436,21 +437,7 @@ SendMediaReady PrepareThemeMedia( MTPVector(), MTP_int(dcId), MTP_vector(attributes)); - - return SendMediaReady( - SendMediaType::ThemeFile, - QString(), // filepath - filename, - content.size(), - content, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - thumbnails, - document, - thumbnailBytes); + return result; } Fn SavePreparedTheme( @@ -570,7 +557,7 @@ Fn SavePreparedTheme( session->mainDcId(), fields.title, theme); - state->filename = media.filename; + state->filename = media->filename; state->themeContent = theme; session->uploader().documentReady( @@ -580,7 +567,7 @@ Fn SavePreparedTheme( uploadTheme(data); }, state->lifetime); - session->uploader().uploadMedia(state->id, media); + session->uploader().upload(state->id, media); }; const auto save = [=] { @@ -999,12 +986,15 @@ ParsedTheme ParseTheme( [[nodiscard]] QString GenerateSlug() { const auto letters = uint8('Z' + 1 - 'A'); const auto digits = uint8('9' + 1 - '0'); + const auto firstValues = uint8(2 * letters); const auto values = uint8(2 * letters + digits); auto result = QString(); result.reserve(kRandomSlugSize); for (auto i = 0; i != kRandomSlugSize; ++i) { - const auto value = base::RandomValue() % values; + const auto value = i + ? (base::RandomValue() % values) + : (base::RandomValue() % firstValues); if (value < letters) { result.append(char('A' + value)); } else if (value < 2 * letters) { From 5b0cac47ad41b6e5e4e4d2f6c195dd67dd120dfc Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 18:37:00 +0400 Subject: [PATCH 37/71] Reapply "Use plain vector for prepared upload parts." This reverts commit f6f8eefaa0e2f149c031fd83bcf0ee3f5759546e. --- Telegram/SourceFiles/storage/file_upload.cpp | 160 ++++++++---------- Telegram/SourceFiles/storage/file_upload.h | 14 +- .../SourceFiles/storage/localimageloader.cpp | 8 +- .../SourceFiles/storage/localimageloader.h | 2 +- 4 files changed, 88 insertions(+), 96 deletions(-) diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index e0c7f8b57..b19af82df 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -65,7 +65,10 @@ struct Uploader::File { bool setPartSize(uint32 partSize); std::shared_ptr file; - int32 partsCount = 0; + const std::vector &parts; + const uint64 partsOfId = 0; + int partsSent = 0; + mutable int64 fileSentSize = 0; HashMd5 md5Hash; @@ -79,11 +82,15 @@ struct Uploader::File { }; Uploader::File::File(const std::shared_ptr &file) -: file(file) { - partsCount = (file->type == SendMediaType::Photo - || file->type == SendMediaType::Secure) - ? file->fileparts.size() - : file->thumbparts.size(); +: file(file) +, parts((file->type == SendMediaType::Photo + || file->type == SendMediaType::Secure) + ? file->fileparts + : file->thumbparts) +, partsOfId((file->type == SendMediaType::Photo + || file->type == SendMediaType::Secure) + ? file->id + : file->thumbId) { if (file->type == SendMediaType::File || file->type == SendMediaType::ThemeFile || file->type == SendMediaType::Audio) { @@ -174,7 +181,7 @@ Uploader::Uploader(not_null api) _api->instance().nonPremiumDelayedRequests( ) | rpl::start_with_next([=](mtpRequestId id) { - if (dcMap.contains(id)) { + if (_dcIndices.contains(id)) { _nonPremiumDelayed.emplace(id); } }, _lifetime); @@ -299,7 +306,7 @@ void Uploader::upload( } void Uploader::currentFailed() { - auto j = queue.find(uploadingId); + auto j = queue.find(_uploadingId); if (j != queue.end()) { const auto [msgId, file] = std::move(*j); queue.erase(j); @@ -307,11 +314,11 @@ void Uploader::currentFailed() { } cancelRequests(); - dcMap.clear(); - uploadingId = FullMsgId(); - sentSize = 0; + _dcIndices.clear(); + _uploadingId = FullMsgId(); + _sentTotal = 0; for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { - sentSizes[i] = 0; + _sentPerDc[i] = 0; } sendNext(); @@ -343,7 +350,7 @@ void Uploader::stopSessions() { } void Uploader::sendNext() { - if (sentSize >= kMaxUploadFileParallelSize || _pausedId.msg) { + if (_sentTotal >= kMaxUploadFileParallelSize || _pausedId.msg) { return; } @@ -358,33 +365,25 @@ void Uploader::sendNext() { if (stopping) { _stopSessionsTimer.cancel(); } - auto i = uploadingId.msg ? queue.find(uploadingId) : queue.begin(); - if (!uploadingId.msg) { - uploadingId = i->first; + auto i = _uploadingId.msg ? queue.find(_uploadingId) : queue.begin(); + if (!_uploadingId.msg) { + _uploadingId = i->first; } else if (i == queue.end()) { i = queue.begin(); - uploadingId = i->first; + _uploadingId = i->first; } auto &uploadingData = i->second; auto todc = 0; for (auto dc = 1; dc != MTP::kUploadSessionsCount; ++dc) { - if (sentSizes[dc] < sentSizes[todc]) { + if (_sentPerDc[dc] < _sentPerDc[todc]) { todc = dc; } } - auto &parts = (uploadingData.file->type == SendMediaType::Photo - || uploadingData.file->type == SendMediaType::Secure) - ? uploadingData.file->fileparts - : uploadingData.file->thumbparts; - const auto partsOfId = (uploadingData.file->type == SendMediaType::Photo - || uploadingData.file->type == SendMediaType::Secure) - ? uploadingData.file->id - : uploadingData.file->thumbId; - if (parts.isEmpty()) { + if (uploadingData.partsSent >= uploadingData.parts.size()) { if (uploadingData.docSentParts >= uploadingData.docPartsCount) { - if (requestsSent.empty() && docRequestsSent.empty()) { + if (_sentSizes.empty()) { const auto options = uploadingData.file ? uploadingData.file->to.options : Api::SendOptions(); @@ -404,11 +403,11 @@ void Uploader::sendNext() { const auto md5 = uploadingData.file->filemd5; const auto file = MTP_inputFile( MTP_long(uploadingData.file->id), - MTP_int(uploadingData.partsCount), + MTP_int(uploadingData.parts.size()), MTP_string(photoFilename), MTP_bytes(md5)); _photoReady.fire({ - .fullId = uploadingId, + .fullId = _uploadingId, .info = { .file = file, .attachedStickers = attachedStickers, @@ -433,19 +432,19 @@ void Uploader::sendNext() { MTP_string(uploadingData.file->filename), MTP_bytes(docMd5)); const auto thumb = [&]() -> std::optional { - if (!uploadingData.partsCount) { + if (uploadingData.parts.empty()) { return std::nullopt; } const auto thumbFilename = uploadingData.file->thumbname; const auto thumbMd5 = uploadingData.file->thumbmd5; return MTP_inputFile( MTP_long(uploadingData.file->thumbId), - MTP_int(uploadingData.partsCount), + MTP_int(uploadingData.parts.size()), MTP_string(thumbFilename), MTP_bytes(thumbMd5)); }(); _documentReady.fire({ - .fullId = uploadingId, + .fullId = _uploadingId, .info = { .file = file, .thumb = thumb, @@ -456,12 +455,13 @@ void Uploader::sendNext() { }); } else if (uploadingData.file->type == SendMediaType::Secure) { _secureReady.fire({ - uploadingId, + _uploadingId, uploadingData.file->id, - uploadingData.partsCount }); + int(uploadingData.parts.size()), + }); } - queue.erase(uploadingId); - uploadingId = FullMsgId(); + queue.erase(_uploadingId); + _uploadingId = FullMsgId(); sendNext(); } return; @@ -522,36 +522,37 @@ void Uploader::sendNext() { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); } - docRequestsSent.emplace(requestId, uploadingData.docSentParts); - dcMap.emplace(requestId, todc); - sentSize += uploadingData.docPartSize; - sentSizes[todc] += uploadingData.docPartSize; + _sentSizes.emplace(requestId, uploadingData.docPartSize); + _docSentRequests.emplace(requestId); + _dcIndices.emplace(requestId, todc); + _sentTotal += uploadingData.docPartSize; + _sentPerDc[todc] += uploadingData.docPartSize; uploadingData.docSentParts++; } else { - auto part = parts.begin(); + const auto index = uploadingData.partsSent++; + const auto partBytes = uploadingData.parts[index]; + const auto partSize = int(partBytes.size()); const auto requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(partsOfId), - MTP_int(part.key()), - MTP_bytes(part.value()) + MTP_long(uploadingData.partsOfId), + MTP_int(index), + MTP_bytes(partBytes) )).done([=](const MTPBool &result, mtpRequestId requestId) { partLoaded(result, requestId); }).fail([=](const MTP::Error &error, mtpRequestId requestId) { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); - requestsSent.emplace(requestId, part.value()); - dcMap.emplace(requestId, todc); - sentSize += part.value().size(); - sentSizes[todc] += part.value().size(); - - parts.erase(part); + _sentSizes.emplace(requestId, partSize); + _dcIndices.emplace(requestId, todc); + _sentTotal += partSize; + _sentPerDc[todc] += partSize; } _nextTimer.callOnce(kUploadRequestInterval); } void Uploader::cancel(const FullMsgId &msgId) { - if (uploadingId == msgId) { + if (_uploadingId == msgId) { currentFailed(); } else { queue.erase(msgId); @@ -559,12 +560,12 @@ void Uploader::cancel(const FullMsgId &msgId) { } void Uploader::cancelAll() { - const auto single = queue.empty() ? uploadingId : queue.begin()->first; + const auto single = queue.empty() ? _uploadingId : queue.begin()->first; if (!single) { return; } _pausedId = single; - if (uploadingId) { + if (_uploadingId) { currentFailed(); } while (!queue.empty()) { @@ -589,61 +590,49 @@ void Uploader::confirm(const FullMsgId &msgId) { } void Uploader::cancelRequests() { - for (const auto &requestData : requestsSent) { + _docSentRequests.clear(); + for (const auto &requestData : _sentSizes) { _api->request(requestData.first).cancel(); } - requestsSent.clear(); - for (const auto &requestData : docRequestsSent) { - _api->request(requestData.first).cancel(); - } - docRequestsSent.clear(); + _sentSizes.clear(); } void Uploader::clear() { queue.clear(); cancelRequests(); - dcMap.clear(); - sentSize = 0; + _dcIndices.clear(); + _sentTotal = 0; for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { _api->instance().stopSession(MTP::uploadDcId(i)); - sentSizes[i] = 0; + _sentPerDc[i] = 0; } _stopSessionsTimer.cancel(); } void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { - auto j = docRequestsSent.end(); - auto i = requestsSent.find(requestId); - if (i == requestsSent.cend()) { - j = docRequestsSent.find(requestId); - } + _docSentRequests.remove(requestId); + auto i = _sentSizes.find(requestId); const auto wasNonPremiumDelayed = _nonPremiumDelayed.remove(requestId); - if (i != requestsSent.cend() || j != docRequestsSent.cend()) { + if (i != _sentSizes.cend()) { if (mtpIsFalse(result)) { // failed to upload current file currentFailed(); return; } else { - auto dcIt = dcMap.find(requestId); - if (dcIt == dcMap.cend()) { // must not happen + auto dcIt = _dcIndices.find(requestId); + if (dcIt == _dcIndices.cend()) { // must not happen currentFailed(); return; } auto dc = dcIt->second; - dcMap.erase(dcIt); + _dcIndices.erase(dcIt); - int64 sentPartSize = 0; - auto k = queue.find(uploadingId); + int64 sentPartSize = i->second; + auto k = queue.find(_uploadingId); Assert(k != queue.cend()); auto &[fullId, file] = *k; - if (i != requestsSent.cend()) { - sentPartSize = i->second.size(); - requestsSent.erase(i); - } else { - sentPartSize = file.docPartSize; - docRequestsSent.erase(j); - } - sentSize -= sentPartSize; - sentSizes[dc] -= sentPartSize; + _sentSizes.erase(i); + _sentTotal -= sentPartSize; + _sentPerDc[dc] -= sentPartSize; if (file.file->type == SendMediaType::Photo) { file.fileSentSize += sentPartSize; const auto photo = session().data().photo(file.file->id); @@ -658,7 +647,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { const auto document = session().data().document(file.file->id); if (document->uploading()) { const auto doneParts = file.docSentParts - - int(docRequestsSent.size()); + - int(_docSentRequests.size()); document->uploadingData->offset = std::min( document->uploadingData->size, doneParts * file.docPartSize); @@ -683,8 +672,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { void Uploader::partFailed(const MTP::Error &error, mtpRequestId requestId) { // failed to upload current file _nonPremiumDelayed.remove(requestId); - if ((requestsSent.find(requestId) != requestsSent.cend()) - || (docRequestsSent.find(requestId) != docRequestsSent.cend())) { + if (_sentSizes.find(requestId) != _sentSizes.cend()) { currentFailed(); } sendNext(); diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index 4347e5c03..a733775ad 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -54,7 +54,7 @@ public: [[nodiscard]] Main::Session &session() const; [[nodiscard]] FullMsgId currentUploadId() const { - return uploadingId; + return _uploadingId; } void upload( @@ -125,14 +125,14 @@ private: int progress = 0); const not_null _api; - base::flat_map requestsSent; - base::flat_map docRequestsSent; - base::flat_map dcMap; + base::flat_map _sentSizes; + base::flat_set _docSentRequests; + base::flat_map _dcIndices; base::flat_set _nonPremiumDelayed; - uint32 sentSize = 0; // FileSize: Right now any file size fits 32 bit. - uint32 sentSizes[MTP::kUploadSessionsCount] = { 0 }; + uint32 _sentTotal = 0; // FileSize: Right now any file size fits 32 bit. + uint32 _sentPerDc[MTP::kUploadSessionsCount] = { 0 }; - FullMsgId uploadingId; + FullMsgId _uploadingId; FullMsgId _pausedId; std::map queue; base::Timer _nextTimer, _stopSessionsTimer; diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 192c7c112..59f2fb505 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -434,8 +434,10 @@ void FilePrepareResult::setFileData(const QByteArray &filedata) { partssize = 0; } else { partssize = filedata.size(); + fileparts.reserve( + (partssize + kPhotoUploadPartSize - 1) / kPhotoUploadPartSize); for (int32 i = 0, part = 0; i < partssize; i += kPhotoUploadPartSize, ++part) { - fileparts.insert(part, filedata.mid(i, kPhotoUploadPartSize)); + fileparts.push_back(filedata.mid(i, kPhotoUploadPartSize)); } filemd5.resize(32); hashMd5Hex(filedata.constData(), filedata.size(), filemd5.data()); @@ -446,8 +448,10 @@ void FilePrepareResult::setThumbData(const QByteArray &thumbdata) { if (!thumbdata.isEmpty()) { thumbbytes = thumbdata; int32 size = thumbdata.size(); + thumbparts.reserve( + (size + kPhotoUploadPartSize - 1) / kPhotoUploadPartSize); for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) { - thumbparts.insert(part, thumbdata.mid(i, kPhotoUploadPartSize)); + thumbparts.push_back(thumbdata.mid(i, kPhotoUploadPartSize)); } thumbmd5.resize(32); hashMd5Hex(thumbdata.constData(), thumbdata.size(), thumbmd5.data()); diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 8b3b7bca7..2756ac244 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -147,7 +147,7 @@ struct FileLoadTo { MsgId replaceMediaOf; }; -using UploadFileParts = QMap; +using UploadFileParts = std::vector; struct FilePrepareDescriptor { TaskId taskId = kEmptyTaskId; base::required id; From 0dcc439ddae2d686fc4b63e521c1d5eb829bc3ef Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 3 Apr 2024 13:49:36 +0400 Subject: [PATCH 38/71] Allow upload requests for several files at once. --- Telegram/SourceFiles/mtproto/facade.h | 7 +- .../SourceFiles/mtproto/session_private.cpp | 3 - Telegram/SourceFiles/storage/file_upload.cpp | 675 ++++++++++-------- Telegram/SourceFiles/storage/file_upload.h | 75 +- 4 files changed, 402 insertions(+), 358 deletions(-) diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index 6f31960da..39e2f732c 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -40,8 +40,6 @@ constexpr ShiftedDcId groupCallStreamDcId(DcId dcId) { return ShiftDcId(dcId, kGroupCallStreamDcShift); } -constexpr auto kUploadSessionsCount = 2; - namespace details { constexpr ShiftedDcId downloadDcId(DcId dcId, int index) { @@ -92,7 +90,6 @@ inline DcId getTemporaryIdFromRealDcId(ShiftedDcId shiftedDcId) { namespace details { constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { - static_assert(kUploadSessionsCount < kMaxMediaDcCount, "Too large MTPUploadSessionsCount!"); return ShiftDcId(dcId, kBaseUploadDcShift + index); }; @@ -101,14 +98,14 @@ constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { // send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id // uploading always to the main dc so BareDcId(result) == 0 inline ShiftedDcId uploadDcId(int index) { - Expects(index >= 0 && index < kUploadSessionsCount); + Expects(index >= 0 && index < kMaxMediaDcCount); return details::uploadDcId(0, index); }; constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) { return (shiftedDcId >= details::uploadDcId(0, 0)) - && (shiftedDcId < details::uploadDcId(0, kUploadSessionsCount - 1) + kDcShift); + && (shiftedDcId < details::uploadDcId(0, kMaxMediaDcCount - 1) + kDcShift); } inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) { diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index 729a95ed6..05e968b7f 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -1108,9 +1108,6 @@ void SessionPrivate::onSentSome(uint64 size) { DEBUG_LOG(("Checking connect for request with size %1 bytes, delay will be %2").arg(size).arg(remain)); } } - if (isUploadDcId(_shiftedDcId)) { - remain *= kUploadSessionsCount; - } _waitForReceivedTimer.callOnce(remain); } if (!_firstSentAt) { diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index b19af82df..b718ed95b 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -27,7 +27,7 @@ namespace Storage { namespace { // max 512kb uploaded at the same time in each session -constexpr auto kMaxUploadFileParallelSize = MTP::kUploadSessionsCount * 512 * 1024; +constexpr auto kMaxUploadPerSession = 512 * 1024; constexpr auto kDocumentMaxPartsCountDefault = 4000; @@ -47,7 +47,7 @@ constexpr auto kDocumentUploadPartSize3 = 256 * 1024; constexpr auto kDocumentUploadPartSize4 = 512 * 1024; // One part each half second, if not uploaded faster. -constexpr auto kUploadRequestInterval = crl::time(500); +constexpr auto kUploadRequestInterval = crl::time(200); // How much time without upload causes additional session kill. constexpr auto kKillSessionTimeout = 15 * crl::time(1000); @@ -58,35 +58,52 @@ constexpr auto kKillSessionTimeout = 15 * crl::time(1000); } // namespace -struct Uploader::File { - explicit File(const std::shared_ptr &file); +struct Uploader::Entry { + Entry(FullMsgId itemId, const std::shared_ptr &file); void setDocSize(int64 size); - bool setPartSize(uint32 partSize); + bool setPartSize(int partSize); + // const, but non-const for the move-assignment in the + FullMsgId itemId; std::shared_ptr file; - const std::vector &parts; - const uint64 partsOfId = 0; - int partsSent = 0; + not_null*> parts; + uint64 partsOfId = 0; - mutable int64 fileSentSize = 0; + int partsSent = 0; + int partsWaiting = 0; + int64 partsSentSize = 0; HashMd5 md5Hash; std::unique_ptr docFile; int64 docSize = 0; - int64 docPartSize = 0; - int docSentParts = 0; + int64 docSentSize = 0; + int docPartSize = 0; + int docPartsSent = 0; int docPartsCount = 0; + int docPartsWaiting = 0; }; -Uploader::File::File(const std::shared_ptr &file) -: file(file) +struct Uploader::Request { + FullMsgId itemId; + crl::time sent = 0; + QByteArray bytes; + int dcIndex = 0; + bool docPart = false; + bool nonPremiumDelayed = false; +}; + +Uploader::Entry::Entry( + FullMsgId itemId, + const std::shared_ptr &file) +: itemId(itemId) +, file(file) , parts((file->type == SendMediaType::Photo || file->type == SendMediaType::Secure) - ? file->fileparts - : file->thumbparts) + ? &file->fileparts + : &file->thumbparts) , partsOfId((file->type == SendMediaType::Photo || file->type == SendMediaType::Secure) ? file->id @@ -95,12 +112,10 @@ Uploader::File::File(const std::shared_ptr &file) || file->type == SendMediaType::ThemeFile || file->type == SendMediaType::Audio) { setDocSize(file->filesize); - } else { - docSize = docPartSize = docPartsCount = 0; } } -void Uploader::File::setDocSize(int64 size) { +void Uploader::Entry::setDocSize(int64 size) { docSize = size; constexpr auto limit0 = 1024 * 1024; constexpr auto limit1 = 32 * limit0; @@ -115,16 +130,15 @@ void Uploader::File::setDocSize(int64 size) { } } -bool Uploader::File::setPartSize(uint32 partSize) { +bool Uploader::Entry::setPartSize(int partSize) { docPartSize = partSize; - docPartsCount = (docSize / docPartSize) - + ((docSize % docPartSize) ? 1 : 0); + docPartsCount = (docSize + docPartSize - 1) / docPartSize; return (docPartsCount <= kDocumentMaxPartsCountDefault); } Uploader::Uploader(not_null api) : _api(api) -, _nextTimer([=] { sendNext(); }) +, _nextTimer([=] { maybeSendNext(); }) , _stopSessionsTimer([=] { stopSessions(); }) { const auto session = &_api->session(); photoReady( @@ -181,22 +195,21 @@ Uploader::Uploader(not_null api) _api->instance().nonPremiumDelayedRequests( ) | rpl::start_with_next([=](mtpRequestId id) { - if (_dcIndices.contains(id)) { - _nonPremiumDelayed.emplace(id); + const auto i = _requests.find(id); + if (i != end(_requests)) { + i->second.nonPremiumDelayed = true; } }, _lifetime); } -void Uploader::processPhotoProgress(const FullMsgId &newId) { - const auto session = &_api->session(); - if (const auto item = session->data().message(newId)) { +void Uploader::processPhotoProgress(FullMsgId itemId) { + if (const auto item = session().data().message(itemId)) { sendProgressUpdate(item, Api::SendProgressType::UploadPhoto); } } -void Uploader::processDocumentProgress(const FullMsgId &newId) { - const auto session = &_api->session(); - if (const auto item = session->data().message(newId)) { +void Uploader::processDocumentProgress(FullMsgId itemId) { + if (const auto item = session().data().message(itemId)) { const auto media = item->media(); const auto document = media ? media->document() : nullptr; const auto sendAction = (document && document->isVoiceMessage()) @@ -210,16 +223,14 @@ void Uploader::processDocumentProgress(const FullMsgId &newId) { } } -void Uploader::processPhotoFailed(const FullMsgId &newId) { - const auto session = &_api->session(); - if (const auto item = session->data().message(newId)) { +void Uploader::processPhotoFailed(FullMsgId itemId) { + if (const auto item = session().data().message(itemId)) { sendProgressUpdate(item, Api::SendProgressType::UploadPhoto, -1); } } -void Uploader::processDocumentFailed(const FullMsgId &newId) { - const auto session = &_api->session(); - if (const auto item = session->data().message(newId)) { +void Uploader::processDocumentFailed(FullMsgId itemId) { + if (const auto item = session().data().message(itemId)) { const auto media = item->media(); const auto document = media ? media->document() : nullptr; const auto sendAction = (document && document->isVoiceMessage()) @@ -254,8 +265,12 @@ Main::Session &Uploader::session() const { return _api->session(); } +FullMsgId Uploader::currentUploadId() const { + return _queue.empty() ? FullMsgId() : _queue.front().itemId; +} + void Uploader::upload( - const FullMsgId &msgId, + FullMsgId itemId, const std::shared_ptr &file) { if (file->type == SendMediaType::Photo) { const auto photo = session().data().processPhoto( @@ -301,211 +316,147 @@ void Uploader::upload( document->checkWallPaperProperties(); } } - queue.emplace(msgId, File(file)); - sendNext(); + _queue.push_back({ itemId, file }); + maybeSendNext(); } -void Uploader::currentFailed() { - auto j = queue.find(_uploadingId); - if (j != queue.end()) { - const auto [msgId, file] = std::move(*j); - queue.erase(j); - notifyFailed(msgId, file); +void Uploader::failed(FullMsgId itemId) { + const auto i = ranges::find(_queue, itemId, &Entry::itemId); + if (i != end(_queue)) { + const auto entry = std::move(*i); + _queue.erase(i); + notifyFailed(entry); } - - cancelRequests(); - _dcIndices.clear(); - _uploadingId = FullMsgId(); - _sentTotal = 0; - for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { - _sentPerDc[i] = 0; - } - - sendNext(); + cancelRequests(itemId); + maybeFinishFront(); + crl::on_main(this, [=] { + maybeSendNext(); + }); } -void Uploader::notifyFailed(FullMsgId id, const File &file) { - const auto type = file.file->type; +void Uploader::notifyFailed(const Entry &entry) { + const auto type = entry.file->type; if (type == SendMediaType::Photo) { - _photoFailed.fire_copy(id); + _photoFailed.fire_copy(entry.itemId); } else if (type == SendMediaType::File || type == SendMediaType::ThemeFile || type == SendMediaType::Audio) { - const auto document = session().data().document(file.file->id); + const auto document = session().data().document(entry.file->id); if (document->uploading()) { document->status = FileUploadFailed; } - _documentFailed.fire_copy(id); + _documentFailed.fire_copy(entry.itemId); } else if (type == SendMediaType::Secure) { - _secureFailed.fire_copy(id); + _secureFailed.fire_copy(entry.itemId); } else { - Unexpected("Type in Uploader::currentFailed."); + Unexpected("Type in Uploader::failed."); } } void Uploader::stopSessions() { - for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { - _api->instance().stopSession(MTP::uploadDcId(i)); + if (ranges::any_of(_sentPerDcIndex, rpl::mappers::_1 != 0)) { + _stopSessionsTimer.callOnce(kKillSessionTimeout); + } else { + for (auto i = 0; i != int(_sentPerDcIndex.size()); ++i) { + _api->instance().stopSession(MTP::uploadDcId(i)); + } + _sentPerDcIndex.clear(); } } -void Uploader::sendNext() { - if (_sentTotal >= kMaxUploadFileParallelSize || _pausedId.msg) { - return; +QByteArray Uploader::readDocPart(not_null entry) { + const auto checked = [&](QByteArray result) { + if ((entry->file->type == SendMediaType::File + || entry->file->type == SendMediaType::ThemeFile + || entry->file->type == SendMediaType::Audio) + && entry->docSize <= kUseBigFilesFrom) { + entry->md5Hash.feed(result.data(), result.size()); + } + if (result.isEmpty() + || (result.size() > entry->docPartSize) + || ((result.size() < entry->docPartSize + && entry->docPartsSent + 1 != entry->docPartsCount))) { + return QByteArray(); + } + return result; + }; + auto &content = entry->file->content; + if (!content.isEmpty()) { + const auto offset = entry->docPartsSent * entry->docPartSize; + return checked(content.mid(offset, entry->docPartSize)); + } else if (!entry->docFile) { + const auto filepath = entry->file->filepath; + entry->docFile = std::make_unique(filepath); + if (!entry->docFile->open(QIODevice::ReadOnly)) { + return QByteArray(); + } } + return checked(entry->docFile->read(entry->docPartSize)); +} +void Uploader::maybeSendNext() { const auto stopping = _stopSessionsTimer.isActive(); - if (queue.empty()) { + if (_queue.empty()) { if (!stopping) { _stopSessionsTimer.callOnce(kKillSessionTimeout); } + _pausedId = FullMsgId(); + return; + } else if (_pausedId) { return; } if (stopping) { _stopSessionsTimer.cancel(); } - auto i = _uploadingId.msg ? queue.find(_uploadingId) : queue.begin(); - if (!_uploadingId.msg) { - _uploadingId = i->first; - } else if (i == queue.end()) { - i = queue.begin(); - _uploadingId = i->first; - } - auto &uploadingData = i->second; auto todc = 0; - for (auto dc = 1; dc != MTP::kUploadSessionsCount; ++dc) { - if (_sentPerDc[dc] < _sentPerDc[todc]) { - todc = dc; + if (_sentPerDcIndex.size() < 2) { + todc = int(_sentPerDcIndex.size()); + _sentPerDcIndex.resize(todc + 1); + } else { + const auto min = ranges::min_element(_sentPerDcIndex); + todc = int(min - begin(_sentPerDcIndex)); + } + const auto alreadySent = _sentPerDcIndex[todc]; + + const auto entry = [&]() -> Entry* { + for (auto i = begin(_queue); i != end(_queue); ++i) { + if (i->partsSent < i->parts->size() + || i->docPartsSent < i->docPartsCount) { + return &*i; + } } + return nullptr; + }(); + if (!entry) { + return; } - if (uploadingData.partsSent >= uploadingData.parts.size()) { - if (uploadingData.docSentParts >= uploadingData.docPartsCount) { - if (_sentSizes.empty()) { - const auto options = uploadingData.file - ? uploadingData.file->to.options - : Api::SendOptions(); - const auto edit = uploadingData.file && - uploadingData.file->to.replaceMediaOf; - const auto attachedStickers = uploadingData.file - ? uploadingData.file->attachedStickers - : std::vector(); - if (uploadingData.file->type == SendMediaType::Photo) { - auto photoFilename = uploadingData.file->filename; - if (!photoFilename.endsWith(u".jpg"_q, Qt::CaseInsensitive)) { - // Server has some extensions checking for inputMediaUploadedPhoto, - // so force the extension to be .jpg anyway. It doesn't matter, - // because the filename from inputFile is not used anywhere. - photoFilename += u".jpg"_q; - } - const auto md5 = uploadingData.file->filemd5; - const auto file = MTP_inputFile( - MTP_long(uploadingData.file->id), - MTP_int(uploadingData.parts.size()), - MTP_string(photoFilename), - MTP_bytes(md5)); - _photoReady.fire({ - .fullId = _uploadingId, - .info = { - .file = file, - .attachedStickers = attachedStickers, - }, - .options = options, - .edit = edit, - }); - } else if (uploadingData.file->type == SendMediaType::File - || uploadingData.file->type == SendMediaType::ThemeFile - || uploadingData.file->type == SendMediaType::Audio) { - QByteArray docMd5(32, Qt::Uninitialized); - hashMd5Hex(uploadingData.md5Hash.result(), docMd5.data()); - - const auto file = (uploadingData.docSize > kUseBigFilesFrom) - ? MTP_inputFileBig( - MTP_long(uploadingData.file->id), - MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.file->filename)) - : MTP_inputFile( - MTP_long(uploadingData.file->id), - MTP_int(uploadingData.docPartsCount), - MTP_string(uploadingData.file->filename), - MTP_bytes(docMd5)); - const auto thumb = [&]() -> std::optional { - if (uploadingData.parts.empty()) { - return std::nullopt; - } - const auto thumbFilename = uploadingData.file->thumbname; - const auto thumbMd5 = uploadingData.file->thumbmd5; - return MTP_inputFile( - MTP_long(uploadingData.file->thumbId), - MTP_int(uploadingData.parts.size()), - MTP_string(thumbFilename), - MTP_bytes(thumbMd5)); - }(); - _documentReady.fire({ - .fullId = _uploadingId, - .info = { - .file = file, - .thumb = thumb, - .attachedStickers = attachedStickers, - }, - .options = options, - .edit = edit, - }); - } else if (uploadingData.file->type == SendMediaType::Secure) { - _secureReady.fire({ - _uploadingId, - uploadingData.file->id, - int(uploadingData.parts.size()), - }); - } - queue.erase(_uploadingId); - _uploadingId = FullMsgId(); - sendNext(); - } + const auto itemId = entry->itemId; + if (entry->partsSent >= entry->parts->size()) { + const auto willProbablyBeSent = entry->docPartSize; + if (alreadySent + willProbablyBeSent >= kMaxUploadPerSession) { return; } - auto &content = uploadingData.file->content; - QByteArray toSend; - if (content.isEmpty()) { - if (!uploadingData.docFile) { - const auto filepath = uploadingData.file->filepath; - uploadingData.docFile = std::make_unique(filepath); - if (!uploadingData.docFile->open(QIODevice::ReadOnly)) { - currentFailed(); - return; - } - } - toSend = uploadingData.docFile->read(uploadingData.docPartSize); - if (uploadingData.docSize <= kUseBigFilesFrom) { - uploadingData.md5Hash.feed(toSend.constData(), toSend.size()); - } - } else { - const auto offset = uploadingData.docSentParts - * uploadingData.docPartSize; - toSend = content.mid(offset, uploadingData.docPartSize); - if ((uploadingData.file->type == SendMediaType::File - || uploadingData.file->type == SendMediaType::ThemeFile - || uploadingData.file->type == SendMediaType::Audio) - && uploadingData.docSentParts <= kUseBigFilesFrom) { - uploadingData.md5Hash.feed(toSend.constData(), toSend.size()); - } - } - if ((toSend.size() > uploadingData.docPartSize) - || ((toSend.size() < uploadingData.docPartSize - && uploadingData.docSentParts + 1 != uploadingData.docPartsCount))) { - currentFailed(); + Assert(entry->docPartsSent < entry->docPartsCount); + + const auto partBytes = readDocPart(entry); + if (partBytes.isEmpty()) { + failed(itemId); return; } + const auto index = entry->docPartsSent++; + ++entry->docPartsWaiting; + mtpRequestId requestId; - if (uploadingData.docSize > kUseBigFilesFrom) { + if (entry->docSize > kUseBigFilesFrom) { requestId = _api->request(MTPupload_SaveBigFilePart( - MTP_long(uploadingData.file->id), - MTP_int(uploadingData.docSentParts), - MTP_int(uploadingData.docPartsCount), - MTP_bytes(toSend) + MTP_long(entry->file->id), + MTP_int(index), + MTP_int(entry->docPartsCount), + MTP_bytes(partBytes) )).done([=](const MTPBool &result, mtpRequestId requestId) { partLoaded(result, requestId); }).fail([=](const MTP::Error &error, mtpRequestId requestId) { @@ -513,29 +464,34 @@ void Uploader::sendNext() { }).toDC(MTP::uploadDcId(todc)).send(); } else { requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(uploadingData.file->id), - MTP_int(uploadingData.docSentParts), - MTP_bytes(toSend) + MTP_long(entry->file->id), + MTP_int(index), + MTP_bytes(partBytes) )).done([=](const MTPBool &result, mtpRequestId requestId) { partLoaded(result, requestId); }).fail([=](const MTP::Error &error, mtpRequestId requestId) { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); } - _sentSizes.emplace(requestId, uploadingData.docPartSize); - _docSentRequests.emplace(requestId); - _dcIndices.emplace(requestId, todc); - _sentTotal += uploadingData.docPartSize; - _sentPerDc[todc] += uploadingData.docPartSize; - - uploadingData.docSentParts++; + _requests.emplace(requestId, Request{ + .itemId = itemId, + .sent = crl::now(), + .bytes = partBytes, + .dcIndex = todc, + .docPart = true, + }); + _sentPerDcIndex[todc] += int(partBytes.size()); } else { - const auto index = uploadingData.partsSent++; - const auto partBytes = uploadingData.parts[index]; - const auto partSize = int(partBytes.size()); + const auto willBeSent = entry->parts->at(entry->partsSent).size(); + if (alreadySent + willBeSent >= kMaxUploadPerSession) { + return; + } + ++entry->partsWaiting; + const auto index = entry->partsSent++; + const auto partBytes = entry->parts->at(index); const auto requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(uploadingData.partsOfId), + MTP_long(entry->partsOfId), MTP_int(index), MTP_bytes(partBytes) )).done([=](const MTPBool &result, mtpRequestId requestId) { @@ -543,139 +499,232 @@ void Uploader::sendNext() { }).fail([=](const MTP::Error &error, mtpRequestId requestId) { partFailed(error, requestId); }).toDC(MTP::uploadDcId(todc)).send(); - _sentSizes.emplace(requestId, partSize); - _dcIndices.emplace(requestId, todc); - _sentTotal += partSize; - _sentPerDc[todc] += partSize; + + _requests.emplace(requestId, Request{ + .itemId = itemId, + .sent = crl::now(), + .bytes = partBytes, + .dcIndex = todc, + }); + _sentPerDcIndex[todc] += int(partBytes.size()); } _nextTimer.callOnce(kUploadRequestInterval); } -void Uploader::cancel(const FullMsgId &msgId) { - if (_uploadingId == msgId) { - currentFailed(); - } else { - queue.erase(msgId); - } +void Uploader::cancel(FullMsgId itemId) { + failed(itemId); } void Uploader::cancelAll() { - const auto single = queue.empty() ? _uploadingId : queue.begin()->first; - if (!single) { - return; - } - _pausedId = single; - if (_uploadingId) { - currentFailed(); - } - while (!queue.empty()) { - const auto [msgId, file] = std::move(*queue.begin()); - queue.erase(queue.begin()); - notifyFailed(msgId, file); + while (!_queue.empty()) { + failed(_queue.front().itemId); } clear(); unpause(); } -void Uploader::pause(const FullMsgId &msgId) { - _pausedId = msgId; +void Uploader::pause(FullMsgId itemId) { + _pausedId = itemId; } void Uploader::unpause() { _pausedId = FullMsgId(); - sendNext(); + maybeSendNext(); } -void Uploader::confirm(const FullMsgId &msgId) { -} - -void Uploader::cancelRequests() { - _docSentRequests.clear(); - for (const auto &requestData : _sentSizes) { - _api->request(requestData.first).cancel(); +void Uploader::cancelRequests(FullMsgId itemId) { + for (auto i = begin(_requests); i != end(_requests);) { + if (i->second.itemId == itemId) { + const auto bytes = int(i->second.bytes.size()); + _sentPerDcIndex[i->second.dcIndex] -= bytes; + _api->request(i->first).cancel(); + i = _requests.erase(i); + } else { + ++i; + } } - _sentSizes.clear(); +} + +void Uploader::cancelAllRequests() { + for (const auto &[requestId, request] : base::take(_requests)) { + _api->request(requestId).cancel(); + } + ranges::fill(_sentPerDcIndex, 0); } void Uploader::clear() { - queue.clear(); - cancelRequests(); - _dcIndices.clear(); - _sentTotal = 0; - for (int i = 0; i < MTP::kUploadSessionsCount; ++i) { - _api->instance().stopSession(MTP::uploadDcId(i)); - _sentPerDc[i] = 0; - } + _queue.clear(); + cancelAllRequests(); + stopSessions(); _stopSessionsTimer.cancel(); } -void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { - _docSentRequests.remove(requestId); - auto i = _sentSizes.find(requestId); - const auto wasNonPremiumDelayed = _nonPremiumDelayed.remove(requestId); - if (i != _sentSizes.cend()) { - if (mtpIsFalse(result)) { // failed to upload current file - currentFailed(); - return; - } else { - auto dcIt = _dcIndices.find(requestId); - if (dcIt == _dcIndices.cend()) { // must not happen - currentFailed(); - return; - } - auto dc = dcIt->second; - _dcIndices.erase(dcIt); +Uploader::Request Uploader::finishRequest(mtpRequestId requestId) { + const auto taken = _requests.take(requestId); + Assert(taken.has_value()); - int64 sentPartSize = i->second; - auto k = queue.find(_uploadingId); - Assert(k != queue.cend()); - auto &[fullId, file] = *k; - _sentSizes.erase(i); - _sentTotal -= sentPartSize; - _sentPerDc[dc] -= sentPartSize; - if (file.file->type == SendMediaType::Photo) { - file.fileSentSize += sentPartSize; - const auto photo = session().data().photo(file.file->id); - if (photo->uploading() && file.file) { - photo->uploadingData->size = file.file->partssize; - photo->uploadingData->offset = file.fileSentSize; - } - _photoProgress.fire_copy(fullId); - } else if (file.file->type == SendMediaType::File - || file.file->type == SendMediaType::ThemeFile - || file.file->type == SendMediaType::Audio) { - const auto document = session().data().document(file.file->id); - if (document->uploading()) { - const auto doneParts = file.docSentParts - - int(_docSentRequests.size()); - document->uploadingData->offset = std::min( - document->uploadingData->size, - doneParts * file.docPartSize); - } - _documentProgress.fire_copy(fullId); - } else if (file.file->type == SendMediaType::Secure) { - file.fileSentSize += sentPartSize; - _secureProgress.fire_copy({ - fullId, - file.fileSentSize, - file.file->partssize }); - } - if (wasNonPremiumDelayed) { - _nonPremiumDelays.fire_copy(fullId); - } - } + _sentPerDcIndex[taken->dcIndex] -= int(taken->bytes.size()); + return *taken; +} + +void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { + const auto request = finishRequest(requestId); + + const auto bytes = int(request.bytes.size()); + const auto itemId = request.itemId; + + if (mtpIsFalse(result)) { // failed to upload current file + failed(itemId); + return; } - sendNext(); + const auto i = ranges::find(_queue, itemId, &Entry::itemId); + Assert(i != end(_queue)); + auto &entry = *i; + + if (request.docPart) { + --entry.docPartsWaiting; + entry.docSentSize += bytes; + } else { + --entry.partsWaiting; + entry.partsSentSize += bytes; + } + + if (entry.file->type == SendMediaType::Photo) { + const auto photo = session().data().photo(entry.file->id); + if (photo->uploading()) { + photo->uploadingData->size = entry.file->partssize; + photo->uploadingData->offset = entry.partsSentSize; + } + _photoProgress.fire_copy(itemId); + } else if (entry.file->type == SendMediaType::File + || entry.file->type == SendMediaType::ThemeFile + || entry.file->type == SendMediaType::Audio) { + const auto document = session().data().document(entry.file->id); + if (document->uploading()) { + document->uploadingData->offset = std::min( + document->uploadingData->size, + entry.docSentSize); + } + _documentProgress.fire_copy(itemId); + } else if (entry.file->type == SendMediaType::Secure) { + _secureProgress.fire_copy({ + .fullId = itemId, + .offset = entry.partsSentSize, + .size = entry.file->partssize, + }); + } + if (request.nonPremiumDelayed) { + _nonPremiumDelays.fire_copy(itemId); + } + + if (!_queue.empty() && itemId == _queue.front().itemId) { + maybeFinishFront(); + } + maybeSendNext(); +} + +void Uploader::maybeFinishFront() { + while (!_queue.empty()) { + const auto &entry = _queue.front(); + if (entry.partsSent >= entry.parts->size() + && entry.docPartsSent >= entry.docPartsCount + && !entry.partsWaiting + && !entry.docPartsWaiting) { + finishFront(); + } else { + break; + } + } +} + +void Uploader::finishFront() { + Expects(!_queue.empty()); + + auto entry = std::move(_queue.front()); + _queue.erase(_queue.begin()); + + const auto options = entry.file + ? entry.file->to.options + : Api::SendOptions(); + const auto edit = entry.file && + entry.file->to.replaceMediaOf; + const auto attachedStickers = entry.file + ? entry.file->attachedStickers + : std::vector(); + if (entry.file->type == SendMediaType::Photo) { + auto photoFilename = entry.file->filename; + if (!photoFilename.endsWith(u".jpg"_q, Qt::CaseInsensitive)) { + // Server has some extensions checking for inputMediaUploadedPhoto, + // so force the extension to be .jpg anyway. It doesn't matter, + // because the filename from inputFile is not used anywhere. + photoFilename += u".jpg"_q; + } + const auto md5 = entry.file->filemd5; + const auto file = MTP_inputFile( + MTP_long(entry.file->id), + MTP_int(entry.parts->size()), + MTP_string(photoFilename), + MTP_bytes(md5)); + _photoReady.fire({ + .fullId = entry.itemId, + .info = { + .file = file, + .attachedStickers = attachedStickers, + }, + .options = options, + .edit = edit, + }); + } else if (entry.file->type == SendMediaType::File + || entry.file->type == SendMediaType::ThemeFile + || entry.file->type == SendMediaType::Audio) { + QByteArray docMd5(32, Qt::Uninitialized); + hashMd5Hex(entry.md5Hash.result(), docMd5.data()); + + const auto file = (entry.docSize > kUseBigFilesFrom) + ? MTP_inputFileBig( + MTP_long(entry.file->id), + MTP_int(entry.docPartsCount), + MTP_string(entry.file->filename)) + : MTP_inputFile( + MTP_long(entry.file->id), + MTP_int(entry.docPartsCount), + MTP_string(entry.file->filename), + MTP_bytes(docMd5)); + const auto thumb = [&]() -> std::optional { + if (entry.parts->empty()) { + return std::nullopt; + } + const auto thumbFilename = entry.file->thumbname; + const auto thumbMd5 = entry.file->thumbmd5; + return MTP_inputFile( + MTP_long(entry.file->thumbId), + MTP_int(entry.parts->size()), + MTP_string(thumbFilename), + MTP_bytes(thumbMd5)); + }(); + _documentReady.fire({ + .fullId = entry.itemId, + .info = { + .file = file, + .thumb = thumb, + .attachedStickers = attachedStickers, + }, + .options = options, + .edit = edit, + }); + } else if (entry.file->type == SendMediaType::Secure) { + _secureReady.fire({ + entry.itemId, + entry.file->id, + int(entry.parts->size()), + }); + } } void Uploader::partFailed(const MTP::Error &error, mtpRequestId requestId) { - // failed to upload current file - _nonPremiumDelayed.remove(requestId); - if (_sentSizes.find(requestId) != _sentSizes.cend()) { - currentFailed(); - } - sendNext(); + const auto request = finishRequest(requestId); + failed(request.itemId); } } // namespace Storage diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index a733775ad..f4fced340 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_common.h" #include "base/timer.h" +#include "base/weak_ptr.h" #include "mtproto/facade.h" class ApiWrap; @@ -46,53 +47,48 @@ struct UploadSecureDone { int partsCount = 0; }; -class Uploader final : public QObject { +class Uploader final : public base::has_weak_ptr { public: explicit Uploader(not_null api); ~Uploader(); [[nodiscard]] Main::Session &session() const; - - [[nodiscard]] FullMsgId currentUploadId() const { - return _uploadingId; - } + [[nodiscard]] FullMsgId currentUploadId() const; void upload( - const FullMsgId &msgId, + FullMsgId itemId, const std::shared_ptr &file); - void cancel(const FullMsgId &msgId); - void pause(const FullMsgId &msgId); - void confirm(const FullMsgId &msgId); - + void pause(FullMsgId itemId); + void cancel(FullMsgId itemId); void cancelAll(); - void clear(); - rpl::producer photoReady() const { + [[nodiscard]] rpl::producer photoReady() const { return _photoReady.events(); } - rpl::producer documentReady() const { + [[nodiscard]] rpl::producer documentReady() const { return _documentReady.events(); } - rpl::producer secureReady() const { + [[nodiscard]] rpl::producer secureReady() const { return _secureReady.events(); } - rpl::producer photoProgress() const { + [[nodiscard]] rpl::producer photoProgress() const { return _photoProgress.events(); } - rpl::producer documentProgress() const { + [[nodiscard]] rpl::producer documentProgress() const { return _documentProgress.events(); } - rpl::producer secureProgress() const { + [[nodiscard]] auto secureProgress() const + -> rpl::producer { return _secureProgress.events(); } - rpl::producer photoFailed() const { + [[nodiscard]] rpl::producer photoFailed() const { return _photoFailed.events(); } - rpl::producer documentFailed() const { + [[nodiscard]] rpl::producer documentFailed() const { return _documentFailed.events(); } - rpl::producer secureFailed() const { + [[nodiscard]] rpl::producer secureFailed() const { return _secureFailed.events(); } @@ -101,23 +97,31 @@ public: } void unpause(); - void sendNext(); void stopSessions(); private: - struct File; + struct Entry; + struct Request; + + [[nodiscard]] QByteArray readDocPart(not_null entry); + void maybeSendNext(); + void maybeFinishFront(); + void finishFront(); void partLoaded(const MTPBool &result, mtpRequestId requestId); void partFailed(const MTP::Error &error, mtpRequestId requestId); + Request finishRequest(mtpRequestId requestId); - void processPhotoProgress(const FullMsgId &msgId); - void processPhotoFailed(const FullMsgId &msgId); - void processDocumentProgress(const FullMsgId &msgId); - void processDocumentFailed(const FullMsgId &msgId); + void processPhotoProgress(FullMsgId itemId); + void processPhotoFailed(FullMsgId itemId); + void processDocumentProgress(FullMsgId itemId); + void processDocumentFailed(FullMsgId itemId); - void notifyFailed(FullMsgId id, const File &file); - void currentFailed(); - void cancelRequests(); + void notifyFailed(const Entry &entry); + void failed(FullMsgId itemId); + void cancelRequests(FullMsgId itemId); + void cancelAllRequests(); + void clear(); void sendProgressUpdate( not_null item, @@ -125,16 +129,13 @@ private: int progress = 0); const not_null _api; - base::flat_map _sentSizes; - base::flat_set _docSentRequests; - base::flat_map _dcIndices; - base::flat_set _nonPremiumDelayed; - uint32 _sentTotal = 0; // FileSize: Right now any file size fits 32 bit. - uint32 _sentPerDc[MTP::kUploadSessionsCount] = { 0 }; - FullMsgId _uploadingId; + std::vector _queue; + + base::flat_map _requests; + std::vector _sentPerDcIndex; + FullMsgId _pausedId; - std::map queue; base::Timer _nextTimer, _stopSessionsTimer; rpl::event_stream _photoReady; From c3f0d2ef311e0ae1cdab9509ecc60ea15db63168 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 3 Apr 2024 19:27:48 +0400 Subject: [PATCH 39/71] Adaptive upload up to 8 sessions. --- Telegram/SourceFiles/storage/file_upload.cpp | 394 +++++++++++++------ Telegram/SourceFiles/storage/file_upload.h | 30 +- 2 files changed, 309 insertions(+), 115 deletions(-) diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index b718ed95b..3028b3e6d 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -52,6 +52,13 @@ constexpr auto kUploadRequestInterval = crl::time(200); // How much time without upload causes additional session kill. constexpr auto kKillSessionTimeout = 15 * crl::time(1000); +// How much wait after session kill before killing another one. +constexpr auto kWaitForNormalizeTimeout = 8 * crl::time(1000); + +constexpr auto kMaxSessionsCount = 8; +constexpr auto kFastRequestThreshold = 1 * crl::time(1000); +constexpr auto kSlowRequestThreshold = 8 * crl::time(1000); + [[nodiscard]] const char *ThumbnailFormat(const QString &mime) { return Core::IsMimeSticker(mime) ? "WEBP" : "JPG"; } @@ -70,9 +77,9 @@ struct Uploader::Entry { not_null*> parts; uint64 partsOfId = 0; - int partsSent = 0; - int partsWaiting = 0; - int64 partsSentSize = 0; + int64 sentSize = 0; + ushort partsSent = 0; + ushort partsWaiting = 0; HashMd5 md5Hash; @@ -80,9 +87,9 @@ struct Uploader::Entry { int64 docSize = 0; int64 docSentSize = 0; int docPartSize = 0; - int docPartsSent = 0; - int docPartsCount = 0; - int docPartsWaiting = 0; + ushort docPartsSent = 0; + ushort docPartsCount = 0; + ushort docPartsWaiting = 0; }; @@ -90,8 +97,10 @@ struct Uploader::Request { FullMsgId itemId; crl::time sent = 0; QByteArray bytes; - int dcIndex = 0; + ushort part = 0; + uchar dcIndex = 0; bool docPart = false; + bool bigPart = false; bool nonPremiumDelayed = false; }; @@ -138,7 +147,7 @@ bool Uploader::Entry::setPartSize(int partSize) { Uploader::Uploader(not_null api) : _api(api) -, _nextTimer([=] { maybeSendNext(); }) +, _nextTimer([=] { maybeSend(); }) , _stopSessionsTimer([=] { stopSessions(); }) { const auto session = &_api->session(); photoReady( @@ -317,7 +326,9 @@ void Uploader::upload( } } _queue.push_back({ itemId, file }); - maybeSendNext(); + if (!_nextTimer.isActive()) { + maybeSend(); + } } void Uploader::failed(FullMsgId itemId) { @@ -330,7 +341,7 @@ void Uploader::failed(FullMsgId itemId) { cancelRequests(itemId); maybeFinishFront(); crl::on_main(this, [=] { - maybeSendNext(); + maybeSend(); }); } @@ -361,6 +372,7 @@ void Uploader::stopSessions() { _api->instance().stopSession(MTP::uploadDcId(i)); } _sentPerDcIndex.clear(); + _dcIndicesWithFastRequests.clear(); } } @@ -394,7 +406,181 @@ QByteArray Uploader::readDocPart(not_null entry) { return checked(entry->docFile->read(entry->docPartSize)); } -void Uploader::maybeSendNext() { +bool Uploader::canAddDcIndex() const { + const auto count = int(_sentPerDcIndex.size()); + return (count < kMaxSessionsCount) + && (count == int(_dcIndicesWithFastRequests.size())); +} + +std::optional Uploader::chooseDcIndexForNextRequest( + const base::flat_set &used) { + for (auto i = 0, count = int(_sentPerDcIndex.size()); i != count; ++i) { + if (!_sentPerDcIndex[i] && !used.contains(i)) { + return i; + } + } + if (canAddDcIndex()) { + const auto result = int(_sentPerDcIndex.size()); + _sentPerDcIndex.push_back(0); + _dcIndicesWithFastRequests.clear(); + _latestDcIndexAdded = crl::now(); + + DEBUG_LOG(("Uploader: Added dc index %1.").arg(result)); + return result; + } + auto result = std::optional(); + for (auto i = 0, count = int(_sentPerDcIndex.size()); i != count; ++i) { + if (!used.contains(i) + && (!result.has_value() + || _sentPerDcIndex[i] < _sentPerDcIndex[*result])) { + result = i; + } + } + return result; +} + +Uploader::Entry *Uploader::chooseEntryForNextRequest() { + if (!_pendingFromRemovedDcIndices.empty()) { + const auto itemId = _pendingFromRemovedDcIndices.front().itemId; + const auto i = ranges::find(_queue, itemId, &Entry::itemId); + Assert(i != end(_queue)); + return &*i; + } + + for (auto i = begin(_queue); i != end(_queue); ++i) { + if (i->partsSent < i->parts->size() + || i->docPartsSent < i->docPartsCount) { + return &*i; + } + } + return nullptr; +} + +auto Uploader::sendPart(not_null entry, uchar dcIndex) +-> SendResult { + return !_pendingFromRemovedDcIndices.empty() + ? sendPendingPart(entry, dcIndex) + : (entry->partsSent < entry->parts->size()) + ? sendSlicedPart(entry, dcIndex) + : sendDocPart(entry, dcIndex); +} + +template +void Uploader::sendPreparedRequest(Prepared &&prepared, Request &&request) { + _sentPerDcIndex[request.dcIndex] += int(request.bytes.size()); + + const auto requestId = _api->request( + std::move(prepared) + ).done([=](const MTPBool &result, mtpRequestId requestId) { + partLoaded(result, requestId); + }).fail([=](const MTP::Error &error, mtpRequestId requestId) { + partFailed(error, requestId); + }).toDC(MTP::uploadDcId(request.dcIndex)).send(); + + request.sent = crl::now(); + _requests.emplace(requestId, std::move(request)); +} + +auto Uploader::sendPendingPart(not_null entry, uchar dcIndex) +-> SendResult { + Expects(!_pendingFromRemovedDcIndices.empty()); + Expects(_pendingFromRemovedDcIndices.front().itemId == entry->itemId); + + auto request = std::move(_pendingFromRemovedDcIndices.front()); + _pendingFromRemovedDcIndices.erase(begin(_pendingFromRemovedDcIndices)); + + const auto part = request.part; + const auto bytes = request.bytes; + request.dcIndex = dcIndex; + if (request.bigPart) { + sendPreparedRequest(MTPupload_SaveBigFilePart( + MTP_long(entry->file->id), + MTP_int(part), + MTP_int(entry->docPartsCount), + MTP_bytes(bytes) + ), std::move(request)); + } else { + const auto id = request.docPart ? entry->file->id : entry->partsOfId; + sendPreparedRequest(MTPupload_SaveFilePart( + MTP_long(id), + MTP_int(part), + MTP_bytes(bytes) + ), std::move(request)); + } + return SendResult::Success; +} + +auto Uploader::sendDocPart(not_null entry, uchar dcIndex) +-> SendResult { + const auto itemId = entry->itemId; + const auto alreadySent = _sentPerDcIndex[dcIndex]; + const auto willProbablyBeSent = entry->docPartSize; + if (alreadySent + willProbablyBeSent >= kMaxUploadPerSession) { + return SendResult::DcIndexFull; + } + + Assert(entry->docPartsSent < entry->docPartsCount); + + const auto partBytes = readDocPart(entry); + if (partBytes.isEmpty()) { + failed(itemId); + return SendResult::Failed; + } + const auto part = entry->docPartsSent++; + ++entry->docPartsWaiting; + + const auto send = [&](auto &&request, bool big) { + sendPreparedRequest(std::move(request), { + .itemId = itemId, + .bytes = partBytes, + .part = part, + .dcIndex = dcIndex, + .docPart = true, + .bigPart = big, + }); + }; + if (entry->docSize > kUseBigFilesFrom) { + send(MTPupload_SaveBigFilePart( + MTP_long(entry->file->id), + MTP_int(part), + MTP_int(entry->docPartsCount), + MTP_bytes(partBytes) + ), true); + } else { + send(MTPupload_SaveFilePart( + MTP_long(entry->file->id), + MTP_int(part), + MTP_bytes(partBytes) + ), false); + } + return SendResult::Success; +} + +auto Uploader::sendSlicedPart(not_null entry, uchar dcIndex) +-> SendResult { + const auto itemId = entry->itemId; + const auto alreadySent = _sentPerDcIndex[dcIndex]; + const auto willBeSent = entry->parts->at(entry->partsSent).size(); + if (alreadySent + willBeSent >= kMaxUploadPerSession) { + return SendResult::DcIndexFull; + } + + ++entry->partsWaiting; + const auto index = entry->partsSent++; + const auto partBytes = entry->parts->at(index); + sendPreparedRequest(MTPupload_SaveFilePart( + MTP_long(entry->partsOfId), + MTP_int(index), + MTP_bytes(partBytes) + ), { + .itemId = itemId, + .bytes = partBytes, + .dcIndex = dcIndex, + }); + return SendResult::Success; +} + +void Uploader::maybeSend() { const auto stopping = _stopSessionsTimer.isActive(); if (_queue.empty()) { if (!stopping) { @@ -404,111 +590,35 @@ void Uploader::maybeSendNext() { return; } else if (_pausedId) { return; - } - - if (stopping) { + } else if (stopping) { _stopSessionsTimer.cancel(); } - auto todc = 0; - if (_sentPerDcIndex.size() < 2) { - todc = int(_sentPerDcIndex.size()); - _sentPerDcIndex.resize(todc + 1); - } else { - const auto min = ranges::min_element(_sentPerDcIndex); - todc = int(min - begin(_sentPerDcIndex)); - } - const auto alreadySent = _sentPerDcIndex[todc]; - - const auto entry = [&]() -> Entry* { - for (auto i = begin(_queue); i != end(_queue); ++i) { - if (i->partsSent < i->parts->size() - || i->docPartsSent < i->docPartsCount) { - return &*i; + auto usedDcIndices = base::flat_set(); + while (true) { + const auto maybeDcIndex = chooseDcIndexForNextRequest(usedDcIndices); + if (!maybeDcIndex.has_value()) { + break; + } + const auto dcIndex = *maybeDcIndex; + while (true) { + const auto entry = chooseEntryForNextRequest(); + if (!entry) { + return; } + const auto result = sendPart(entry, dcIndex); + if (result == SendResult::DcIndexFull) { + return; + } else if (result == SendResult::Success) { + break; + } + // If this entry failed, we try the next one. } - return nullptr; - }(); - if (!entry) { - return; + usedDcIndices.emplace(dcIndex); } - - const auto itemId = entry->itemId; - if (entry->partsSent >= entry->parts->size()) { - const auto willProbablyBeSent = entry->docPartSize; - if (alreadySent + willProbablyBeSent >= kMaxUploadPerSession) { - return; - } - - Assert(entry->docPartsSent < entry->docPartsCount); - - const auto partBytes = readDocPart(entry); - if (partBytes.isEmpty()) { - failed(itemId); - return; - } - const auto index = entry->docPartsSent++; - ++entry->docPartsWaiting; - - mtpRequestId requestId; - if (entry->docSize > kUseBigFilesFrom) { - requestId = _api->request(MTPupload_SaveBigFilePart( - MTP_long(entry->file->id), - MTP_int(index), - MTP_int(entry->docPartsCount), - MTP_bytes(partBytes) - )).done([=](const MTPBool &result, mtpRequestId requestId) { - partLoaded(result, requestId); - }).fail([=](const MTP::Error &error, mtpRequestId requestId) { - partFailed(error, requestId); - }).toDC(MTP::uploadDcId(todc)).send(); - } else { - requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(entry->file->id), - MTP_int(index), - MTP_bytes(partBytes) - )).done([=](const MTPBool &result, mtpRequestId requestId) { - partLoaded(result, requestId); - }).fail([=](const MTP::Error &error, mtpRequestId requestId) { - partFailed(error, requestId); - }).toDC(MTP::uploadDcId(todc)).send(); - } - _requests.emplace(requestId, Request{ - .itemId = itemId, - .sent = crl::now(), - .bytes = partBytes, - .dcIndex = todc, - .docPart = true, - }); - _sentPerDcIndex[todc] += int(partBytes.size()); - } else { - const auto willBeSent = entry->parts->at(entry->partsSent).size(); - if (alreadySent + willBeSent >= kMaxUploadPerSession) { - return; - } - - ++entry->partsWaiting; - const auto index = entry->partsSent++; - const auto partBytes = entry->parts->at(index); - const auto requestId = _api->request(MTPupload_SaveFilePart( - MTP_long(entry->partsOfId), - MTP_int(index), - MTP_bytes(partBytes) - )).done([=](const MTPBool &result, mtpRequestId requestId) { - partLoaded(result, requestId); - }).fail([=](const MTP::Error &error, mtpRequestId requestId) { - partFailed(error, requestId); - }).toDC(MTP::uploadDcId(todc)).send(); - - _requests.emplace(requestId, Request{ - .itemId = itemId, - .sent = crl::now(), - .bytes = partBytes, - .dcIndex = todc, - }); - _sentPerDcIndex[todc] += int(partBytes.size()); + if (!usedDcIndices.empty()) { + _nextTimer.callOnce(kUploadRequestInterval); } - _nextTimer.callOnce(kUploadRequestInterval); } void Uploader::cancel(FullMsgId itemId) { @@ -529,7 +639,7 @@ void Uploader::pause(FullMsgId itemId) { void Uploader::unpause() { _pausedId = FullMsgId(); - maybeSendNext(); + maybeSend(); } void Uploader::cancelRequests(FullMsgId itemId) { @@ -543,6 +653,11 @@ void Uploader::cancelRequests(FullMsgId itemId) { ++i; } } + _pendingFromRemovedDcIndices.erase(ranges::remove( + _pendingFromRemovedDcIndices, + itemId, + &Request::itemId + ), end(_pendingFromRemovedDcIndices)); } void Uploader::cancelAllRequests() { @@ -582,19 +697,48 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { Assert(i != end(_queue)); auto &entry = *i; + const auto now = crl::now(); + const auto duration = now - request.sent; + const auto fast = (duration < kFastRequestThreshold); + const auto slowish = !fast; + const auto slow = (duration >= kSlowRequestThreshold); + + if (slowish) { + _dcIndicesWithFastRequests.clear(); + if (slow) { + const auto elapsed = (now - _latestDcIndexRemoved); + const auto remove = (elapsed >= kWaitForNormalizeTimeout); + if (remove && _sentPerDcIndex.size() > 1) { + DEBUG_LOG(("Uploader: Slow request, removing dc index.")); + removeDcIndex(); + _latestDcIndexRemoved = now; + } else { + DEBUG_LOG(("Uploader: Slow request, clear fast records.")); + } + } else { + DEBUG_LOG(("Uploader: Slow-ish request, clear fast records.")); + } + } else if (request.sent > _latestDcIndexAdded) { + if (_dcIndicesWithFastRequests.emplace(request.dcIndex).second) { + DEBUG_LOG(("Uploader: Mark %1 of %2 as fast." + ).arg(request.dcIndex + ).arg(_sentPerDcIndex.size())); + } + } + if (request.docPart) { --entry.docPartsWaiting; entry.docSentSize += bytes; } else { --entry.partsWaiting; - entry.partsSentSize += bytes; + entry.sentSize += bytes; } if (entry.file->type == SendMediaType::Photo) { const auto photo = session().data().photo(entry.file->id); if (photo->uploading()) { photo->uploadingData->size = entry.file->partssize; - photo->uploadingData->offset = entry.partsSentSize; + photo->uploadingData->offset = entry.sentSize; } _photoProgress.fire_copy(itemId); } else if (entry.file->type == SendMediaType::File @@ -610,7 +754,7 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } else if (entry.file->type == SendMediaType::Secure) { _secureProgress.fire_copy({ .fullId = itemId, - .offset = entry.partsSentSize, + .offset = entry.sentSize, .size = entry.file->partssize, }); } @@ -621,7 +765,29 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { if (!_queue.empty() && itemId == _queue.front().itemId) { maybeFinishFront(); } - maybeSendNext(); + maybeSend(); +} + +void Uploader::removeDcIndex() { + Expects(_sentPerDcIndex.size() > 1); + + const auto dcIndex = int(_sentPerDcIndex.size()) - 1; + for (auto i = begin(_requests); i != end(_requests);) { + if (i->second.dcIndex == dcIndex) { + const auto bytes = int(i->second.bytes.size()); + _sentPerDcIndex[dcIndex] -= bytes; + _api->request(i->first).cancel(); + _pendingFromRemovedDcIndices.push_back(std::move(i->second)); + i = _requests.erase(i); + } else { + ++i; + } + } + Assert(_sentPerDcIndex.back() == 0); + _sentPerDcIndex.pop_back(); + _dcIndicesWithFastRequests.remove(dcIndex); + _api->instance().stopSession(MTP::uploadDcId(dcIndex)); + DEBUG_LOG(("Uploader: Removed dc index %1.").arg(dcIndex)); } void Uploader::maybeFinishFront() { diff --git a/Telegram/SourceFiles/storage/file_upload.h b/Telegram/SourceFiles/storage/file_upload.h index f4fced340..6f87f6a56 100644 --- a/Telegram/SourceFiles/storage/file_upload.h +++ b/Telegram/SourceFiles/storage/file_upload.h @@ -103,8 +103,30 @@ private: struct Entry; struct Request; + enum class SendResult : uchar { + Success, + Failed, + DcIndexFull, + }; + + void maybeSend(); + [[nodiscard]] bool canAddDcIndex() const; + [[nodiscard]] std::optional chooseDcIndexForNextRequest( + const base::flat_set &used); + [[nodiscard]] Entry *chooseEntryForNextRequest(); + [[nodiscard]] SendResult sendPart(not_null entry, uchar dcIndex); + [[nodiscard]] auto sendPendingPart(not_null entry, uchar dcIndex) + -> SendResult; + [[nodiscard]] auto sendDocPart(not_null entry, uchar dcIndex) + -> SendResult; + [[nodiscard]] auto sendSlicedPart(not_null entry, uchar dcIndex) + -> SendResult; [[nodiscard]] QByteArray readDocPart(not_null entry); - void maybeSendNext(); + void removeDcIndex(); + + template + void sendPreparedRequest(Prepared &&prepared, Request &&request); + void maybeFinishFront(); void finishFront(); @@ -135,6 +157,12 @@ private: base::flat_map _requests; std::vector _sentPerDcIndex; + // Fast requests since the latest dc index addition. + base::flat_set _dcIndicesWithFastRequests; + crl::time _latestDcIndexAdded = 0; + crl::time _latestDcIndexRemoved = 0; + std::vector _pendingFromRemovedDcIndices; + FullMsgId _pausedId; base::Timer _nextTimer, _stopSessionsTimer; From a9c1970f414ddd5c0529a04f41d91705464fe381 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 13:50:28 +0400 Subject: [PATCH 40/71] Send up to 1MB of parts to a single session. --- .../SourceFiles/mtproto/session_private.cpp | 62 +++++++++++++------ Telegram/SourceFiles/storage/file_upload.cpp | 29 ++++++--- 2 files changed, 65 insertions(+), 26 deletions(-) diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index 05e968b7f..09f078f87 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -60,6 +60,8 @@ constexpr auto kSendStateRequestWaiting = crl::time(1000); // How much time to wait for some more requests, when sending msg acks. constexpr auto kAckSendWaiting = 10 * crl::time(1000); +constexpr auto kCutContainerOnSize = 16 * 1024; + auto SyncTimeRequestDuration = kFastRequestDuration; using namespace details; @@ -696,7 +698,8 @@ void SessionPrivate::tryToSend() { initSize = initSizeInInts * sizeof(mtpPrime); } - bool needAnyResponse = false; + auto needAnyResponse = false; + auto someSkipped = false; SerializedRequest toSendRequest; { QWriteLocker locker1(_sessionData->toSendMutex()); @@ -711,15 +714,33 @@ void SessionPrivate::tryToSend() { locker1.unlock(); } - uint32 toSendCount = toSend.size(); - if (pingRequest) ++toSendCount; - if (ackRequest) ++toSendCount; - if (resendRequest) ++toSendCount; - if (stateRequest) ++toSendCount; - if (httpWaitRequest) ++toSendCount; - if (bindDcKeyRequest) ++toSendCount; + auto totalSending = int(toSend.size()); + auto sendingFrom = begin(toSend); + auto sendingTill = end(toSend); + auto combinedLength = 0; + for (auto i = sendingFrom; i != sendingTill; ++i) { + combinedLength += i->second->size(); + if (combinedLength >= kCutContainerOnSize) { + ++i; + if (const auto skipping = int(sendingTill - i)) { + sendingTill = i; + totalSending -= skipping; + Assert(totalSending > 0); + someSkipped = true; + } + break; + } + } + auto sendingRange = ranges::make_subrange(sendingFrom, sendingTill); + const auto sendingCount = totalSending; + if (pingRequest) ++totalSending; + if (ackRequest) ++totalSending; + if (resendRequest) ++totalSending; + if (stateRequest) ++totalSending; + if (httpWaitRequest) ++totalSending; + if (bindDcKeyRequest) ++totalSending; - if (!toSendCount) { + if (!totalSending) { return; // nothing to send } @@ -735,11 +756,11 @@ void SessionPrivate::tryToSend() { ? httpWaitRequest : bindDcKeyRequest ? bindDcKeyRequest - : toSend.begin()->second; - if (toSendCount == 1 && !first->forceSendInContainer) { + : sendingRange.begin()->second; + if (totalSending == 1 && !first->forceSendInContainer) { toSendRequest = first; if (sendAll) { - toSend.clear(); + toSend.erase(sendingFrom, sendingTill); locker1.unlock(); } @@ -808,7 +829,7 @@ void SessionPrivate::tryToSend() { if (stateRequest) containerSize += stateRequest.messageSize(); if (httpWaitRequest) containerSize += httpWaitRequest.messageSize(); if (bindDcKeyRequest) containerSize += bindDcKeyRequest.messageSize(); - for (const auto &[requestId, request] : toSend) { + for (const auto &[requestId, request] : sendingRange) { containerSize += request.messageSize(); if (needsLayer && request->needsLayer) { containerSize += initSizeInInts; @@ -825,9 +846,9 @@ void SessionPrivate::tryToSend() { // prepare container + each in invoke after toSendRequest = SerializedRequest::Prepare( containerSize, - containerSize + 3 * toSend.size()); + containerSize + 3 * sendingCount); toSendRequest->push_back(mtpc_msg_container); - toSendRequest->push_back(toSendCount); + toSendRequest->push_back(totalSending); // check for a valid container auto bigMsgId = base::unixtime::mtproto_msg_id(); @@ -839,7 +860,7 @@ void SessionPrivate::tryToSend() { // prepare sent container auto sentIdsWrap = SentContainer(); sentIdsWrap.sent = crl::now(); - sentIdsWrap.messages.reserve(toSendCount); + sentIdsWrap.messages.reserve(totalSending); if (bindDcKeyRequest) { _bindMsgId = placeToContainer( @@ -859,7 +880,7 @@ void SessionPrivate::tryToSend() { needAnyResponse = true; } - for (auto &[requestId, request] : toSend) { + for (auto &[requestId, request] : sendingRange) { const auto msgId = prepareToSend( request, bigMsgId, @@ -904,7 +925,7 @@ void SessionPrivate::tryToSend() { memcpy(toSendRequest->data() + from, request->constData() + 4, len * sizeof(mtpPrime)); } } - toSend.clear(); + toSend.erase(sendingFrom, sendingTill); if (stateRequest) { const auto msgId = placeToContainer( @@ -951,6 +972,11 @@ void SessionPrivate::tryToSend() { } } sendSecureRequest(std::move(toSendRequest), needAnyResponse); + if (someSkipped) { + InvokeQueued(this, [=] { + tryToSend(); + }); + } } void SessionPrivate::retryByTimer() { diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index 3028b3e6d..95c4b0a1b 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -26,8 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Storage { namespace { -// max 512kb uploaded at the same time in each session -constexpr auto kMaxUploadPerSession = 512 * 1024; +// max 1mb uploaded at the same time in each session +constexpr auto kMaxUploadPerSession = 1024 * 1024; constexpr auto kDocumentMaxPartsCountDefault = 4000; @@ -47,7 +47,7 @@ constexpr auto kDocumentUploadPartSize3 = 256 * 1024; constexpr auto kDocumentUploadPartSize4 = 512 * 1024; // One part each half second, if not uploaded faster. -constexpr auto kUploadRequestInterval = crl::time(200); +constexpr auto kUploadRequestInterval = crl::time(250); // How much time without upload causes additional session kill. constexpr auto kKillSessionTimeout = 15 * crl::time(1000); @@ -59,6 +59,10 @@ constexpr auto kMaxSessionsCount = 8; constexpr auto kFastRequestThreshold = 1 * crl::time(1000); constexpr auto kSlowRequestThreshold = 8 * crl::time(1000); +// Request is 'fast' if it was done in less than 1s and +// (it-s size + queued before size) >= 512kb. +constexpr auto kAcceptAsFastIfTotalAtLeast = 512 * 1024; + [[nodiscard]] const char *ThumbnailFormat(const QString &mime) { return Core::IsMimeSticker(mime) ? "WEBP" : "JPG"; } @@ -97,6 +101,7 @@ struct Uploader::Request { FullMsgId itemId; crl::time sent = 0; QByteArray bytes; + int queued = 0; ushort part = 0; uchar dcIndex = 0; bool docPart = false; @@ -467,7 +472,9 @@ auto Uploader::sendPart(not_null entry, uchar dcIndex) template void Uploader::sendPreparedRequest(Prepared &&prepared, Request &&request) { - _sentPerDcIndex[request.dcIndex] += int(request.bytes.size()); + auto &sentInSession = _sentPerDcIndex[request.dcIndex]; + const auto queued = sentInSession; + sentInSession += int(request.bytes.size()); const auto requestId = _api->request( std::move(prepared) @@ -478,6 +485,7 @@ void Uploader::sendPreparedRequest(Prepared &&prepared, Request &&request) { }).toDC(MTP::uploadDcId(request.dcIndex)).send(); request.sent = crl::now(); + request.queued = queued; _requests.emplace(requestId, std::move(request)); } @@ -515,7 +523,7 @@ auto Uploader::sendDocPart(not_null entry, uchar dcIndex) const auto itemId = entry->itemId; const auto alreadySent = _sentPerDcIndex[dcIndex]; const auto willProbablyBeSent = entry->docPartSize; - if (alreadySent + willProbablyBeSent >= kMaxUploadPerSession) { + if (alreadySent + willProbablyBeSent > kMaxUploadPerSession) { return SendResult::DcIndexFull; } @@ -614,9 +622,13 @@ void Uploader::maybeSend() { } // If this entry failed, we try the next one. } - usedDcIndices.emplace(dcIndex); + if (_sentPerDcIndex[dcIndex] >= kAcceptAsFastIfTotalAtLeast) { + usedDcIndices.emplace(dcIndex); + } } - if (!usedDcIndices.empty()) { + if (usedDcIndices.empty()) { + _nextTimer.cancel(); + } else { _nextTimer.callOnce(kUploadRequestInterval); } } @@ -718,7 +730,8 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) { } else { DEBUG_LOG(("Uploader: Slow-ish request, clear fast records.")); } - } else if (request.sent > _latestDcIndexAdded) { + } else if (request.sent > _latestDcIndexAdded + && (request.queued + bytes >= kAcceptAsFastIfTotalAtLeast)) { if (_dcIndicesWithFastRequests.emplace(request.dcIndex).second) { DEBUG_LOG(("Uploader: Mark %1 of %2 as fast." ).arg(request.dcIndex From 8df6d9db7ee5020c1a54c913a5cb929f51515559 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 4 Apr 2024 17:37:50 +0400 Subject: [PATCH 41/71] Don't update server-time too frequently. --- Telegram/SourceFiles/mtproto/session_private.cpp | 6 ++++-- Telegram/lib_base | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/mtproto/session_private.cpp b/Telegram/SourceFiles/mtproto/session_private.cpp index 09f078f87..bdd2b9a83 100644 --- a/Telegram/SourceFiles/mtproto/session_private.cpp +++ b/Telegram/SourceFiles/mtproto/session_private.cpp @@ -1651,7 +1651,9 @@ SessionPrivate::HandleResult SessionPrivate::handleOneReceived( } _sessionSalt = data.vnew_server_salt().v; - correctUnixtimeWithBadLocal(info.serverTime); + + // Don't force time update here. + base::unixtime::update(info.serverTime); if (_bindMsgId) { LOG(("Message Info: bad_server_salt received while binding temp key, restarting.")); @@ -2079,7 +2081,7 @@ void SessionPrivate::correctUnixtimeByFastRequest( locker.unlock(); SyncTimeRequestDuration = duration; - base::unixtime::update(serverTime, true); + base::unixtime::update(serverTime); return; } } diff --git a/Telegram/lib_base b/Telegram/lib_base index beef65fac..3d1ea724a 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit beef65fac578a812dade36557f4c666ac3d3ba78 +Subproject commit 3d1ea724a80c3e3a431a88a653d24f4f418eaea8 From c5847caa91dc89672717e380038386840be85b05 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Apr 2024 14:42:22 +0400 Subject: [PATCH 42/71] Update FFmpeg to 6.1.1: Fix AV1 videos. --- Telegram/build/docker/centos_env/Dockerfile | 4 ++-- Telegram/build/prepare/prepare.py | 24 ++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index f5f7ab065..2b5cf11cd 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -479,7 +479,7 @@ RUN git clone -b 1.19.0 --depth=1 {{ GIT_FREEDESKTOP }}/wayland.git \ && rm -rf wayland FROM builder AS nv-codec-headers -RUN git clone -b n12.0.16.0 --depth=1 {{ GIT }}/FFmpeg/nv-codec-headers.git \ +RUN git clone -b n12.1.14.0 --depth=1 {{ GIT }}/FFmpeg/nv-codec-headers.git \ && DESTDIR="{{ LibrariesPath }}/nv-codec-headers-cache" make -C nv-codec-headers install \ && rm -rf nv-codec-headers @@ -491,7 +491,7 @@ COPY --link --from=libXext {{ LibrariesPath }}/libXext-cache / COPY --link --from=libXv {{ LibrariesPath }}/libXv-cache / COPY --link --from=nv-codec-headers {{ LibrariesPath }}/nv-codec-headers-cache / -RUN git clone -b n5.1.3 --depth=1 {{ GIT }}/FFmpeg/FFmpeg.git \ +RUN git clone -b n6.1.1 --depth=1 {{ GIT }}/FFmpeg/FFmpeg.git \ && cd FFmpeg \ && ./configure \ --extra-cflags="-DCONFIG_SAFE_BITSTREAM_READER=1" \ diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 8d472eef3..9815fbe18 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -425,7 +425,7 @@ if customRunCommand: stage('patches', """ git clone https://github.com/desktop-app/patches.git cd patches - git checkout cc0c2f8365 + git checkout 1fef4b342a """) stage('msys64', """ @@ -696,14 +696,17 @@ mac: make install """) +# Somehow in x86 Debug build dav1d crashes on AV1 10bpc videos. stage('dav1d', """ - git clone -b 1.4.1 --depth 1 https://code.videolan.org/videolan/dav1d.git + git clone -b 1.4.1 https://code.videolan.org/videolan/dav1d.git cd dav1d win: if "%X8664%" equ "x64" ( SET "TARGET=x86_64" + SET "DAV1D_ASM_DISABLE=" ) else ( SET "TARGET=x86" + SET "DAV1D_ASM_DISABLE=-Denable_asm=false" ) set FILE=cross-file.txt echo [binaries] > %FILE% @@ -719,7 +722,7 @@ win: depends:python/Scripts/activate.bat %THIRDPARTY_DIR%\\python\\Scripts\\activate.bat - meson setup --cross-file %FILE% --prefix %LIBS_DIR%/local --default-library=static --buildtype=debug -Denable_tools=false -Denable_tests=false -Db_vscrt=mtd builddir-debug + meson setup --cross-file %FILE% --prefix %LIBS_DIR%/local --default-library=static --buildtype=debug -Denable_tools=false -Denable_tests=false %DAV1D_ASM_DISABLE% -Db_vscrt=mtd builddir-debug meson compile -C builddir-debug meson install -C builddir-debug release: @@ -775,7 +778,7 @@ mac: """) stage('libavif', """ - git clone -b v1.0.4 --depth 1 https://github.com/AOMediaCodec/libavif.git + git clone -b v1.0.4 https://github.com/AOMediaCodec/libavif.git cd libavif win: cmake . ^ @@ -805,7 +808,7 @@ mac: """) stage('libde265', """ - git clone --depth 1 -b v1.0.15 https://github.com/strukturag/libde265.git + git clone -b v1.0.15 https://github.com/strukturag/libde265.git cd libde265 win: cmake . ^ @@ -881,7 +884,7 @@ mac: """) stage('libheif', """ - git clone --depth 1 -b v1.17.6 https://github.com/strukturag/libheif.git + git clone -b v1.17.6 https://github.com/strukturag/libheif.git cd libheif win: %THIRDPARTY_DIR%\\msys64\\usr\\bin\\sed.exe -i 's/LIBHEIF_EXPORTS/LIBDE265_STATIC_BUILD/g' libheif/CMakeLists.txt @@ -933,7 +936,7 @@ mac: """) stage('libjxl', """ - git clone -b v0.8.2 --depth 1 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git + git clone -b v0.8.2 --recursive --shallow-submodules https://github.com/libjxl/libjxl.git cd libjxl """ + setVar("cmake_defines", """ -DBUILD_SHARED_LIBS=OFF @@ -1052,9 +1055,7 @@ depends:yasm/yasm stage('nv-codec-headers', """ win: - git clone https://github.com/FFmpeg/nv-codec-headers.git - cd nv-codec-headers - git checkout n11.1.5.1 + git clone -b n12.1.14.0 https://github.com/FFmpeg/nv-codec-headers.git """) stage('regex', """ @@ -1062,9 +1063,8 @@ stage('regex', """ """) stage('ffmpeg', """ - git clone https://github.com/FFmpeg/FFmpeg.git ffmpeg + git clone -b n6.1.1 https://github.com/FFmpeg/FFmpeg.git ffmpeg cd ffmpeg - git checkout 7268323193 win: SET PATH_BACKUP_=%PATH% SET PATH=%ROOT_DIR%\\ThirdParty\\msys64\\usr\\bin;%PATH% From ad3f705f5060305f053dcd88263e87d2dadb8429 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Apr 2024 15:35:01 +0400 Subject: [PATCH 43/71] Beta version 4.16.3. - Improve media upload speed. - Update FFmpeg to 6.1.1. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 6 +++--- Telegram/build/version | 10 +++++----- changelog.txt | 5 +++++ 6 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 821e6eb70..16a9f2e56 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.16.3.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index d2f50744e..09de12abf 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,2,0 - PRODUCTVERSION 4,16,2,0 + FILEVERSION 4,16,3,0 + PRODUCTVERSION 4,16,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.16.2.0" + VALUE "FileVersion", "4.16.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.2.0" + VALUE "ProductVersion", "4.16.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index a395fa632..f70a9486b 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,2,0 - PRODUCTVERSION 4,16,2,0 + FILEVERSION 4,16,3,0 + PRODUCTVERSION 4,16,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.16.2.0" + VALUE "FileVersion", "4.16.3.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.2.0" + VALUE "ProductVersion", "4.16.3.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index beb3cc33b..76e05eb4a 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 = 4016002; -constexpr auto AppVersionStr = "4.16.2"; -constexpr auto AppBetaVersion = false; +constexpr auto AppVersion = 4016003; +constexpr auto AppVersionStr = "4.16.3"; +constexpr auto AppBetaVersion = true; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index 20d886536..b41f8cd69 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4016002 +AppVersion 4016003 AppVersionStrMajor 4.16 -AppVersionStrSmall 4.16.2 -AppVersionStr 4.16.2 -BetaChannel 0 +AppVersionStrSmall 4.16.3 +AppVersionStr 4.16.3 +BetaChannel 1 AlphaVersion 0 -AppVersionOriginal 4.16.2 +AppVersionOriginal 4.16.3.beta diff --git a/changelog.txt b/changelog.txt index a0808da30..d792c1391 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +4.16.3 beta (05.04.24) + +- Improve media upload speed. +- Update FFmpeg to 6.1.1. + 4.16.2 (04.04.24) - Use IV by default for Telegraph and some Telegram links. From 0ce6a4cbdb1e01d0a783881153f77e76e5ed2784 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Apr 2024 20:48:06 +0400 Subject: [PATCH 44/71] Beta version 4.16.3: Re-enable system proxy (Linux). This reverts commit 1e6fb202f0b2964b44719ce671dd90d2cb4889dd. --- Telegram/SourceFiles/core/sandbox.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index fb145f7f1..6feacdb5b 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -33,7 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include -#include namespace Core { namespace { @@ -518,10 +517,8 @@ void Sandbox::refreshGlobalProxy() { || proxy.type == MTP::ProxyData::Type::Http) { QNetworkProxy::setApplicationProxy( MTP::ToNetworkProxy(MTP::ToDirectIpProxy(proxy))); - } else if ((!Core::IsAppLaunched() - || Core::App().settings().proxy().isSystem()) - // this works stable only in sandboxed environment where it works through portal - && (!Platform::IsLinux() || KSandbox::isInside() || cDebugMode())) { + } else if (!Core::IsAppLaunched() + || Core::App().settings().proxy().isSystem()) { QNetworkProxyFactory::setUseSystemConfiguration(true); } else { QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); From 8f8725e1af92fb6b8c605fe290ab04b424734cc5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Apr 2024 21:25:04 +0400 Subject: [PATCH 45/71] Beta version 4.16.3: Fix dav1d in FFmpeg on macOS. --- Telegram/build/prepare/prepare.py | 2 ++ cmake | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/build/prepare/prepare.py b/Telegram/build/prepare/prepare.py index 9815fbe18..8188b10f1 100644 --- a/Telegram/build/prepare/prepare.py +++ b/Telegram/build/prepare/prepare.py @@ -1095,6 +1095,7 @@ depends:yasm/yasm --disable-network \ --disable-everything \ --enable-protocol=file \ + --enable-libdav1d \ --enable-libopus \ --enable-libvpx \ --enable-hwaccel=h264_videotoolbox \ @@ -1116,6 +1117,7 @@ depends:yasm/yasm --enable-decoder=gif \ --enable-decoder=h264 \ --enable-decoder=hevc \ + --enable-decoder=libdav1d \ --enable-decoder=libvpx_vp8 \ --enable-decoder=libvpx_vp9 \ --enable-decoder=mp1 \ diff --git a/cmake b/cmake index 9f1b85aa9..022c15d43 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 9f1b85aa9d51025ccefe63992d05c878bc267774 +Subproject commit 022c15d437aba149b1495532b1560de2a71b13df From 9251e6faba96b4c27ff987c793cb8d26572dfa4f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Apr 2024 17:30:41 +0400 Subject: [PATCH 46/71] Use better title in boost box. --- Telegram/SourceFiles/ui/boxes/boost_box.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp index 008726ee6..691d84f16 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp @@ -397,7 +397,9 @@ void BoostBox( close->parentWidget(), MakeTitle( box, - rpl::duplicate(title), + (data.group + ? tr::lng_boost_group_button + : tr::lng_boost_channel_button)(), rpl::duplicate(repeated), false)); const auto titleInner = faded.data(); From 86d0c49e447ae147d42edea63b9e1bcb61eca979 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Apr 2024 17:49:22 +0400 Subject: [PATCH 47/71] Add "Boost Group/Channel" button to chat menu. --- .../SourceFiles/window/window_peer_menu.cpp | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index d625b739b..c3c0950c2 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -300,6 +300,7 @@ private: void addDeleteTopic(); void addVideoChat(); void addViewStatistics(); + void addBoostChat(); not_null _controller; Dialogs::EntryState _request; @@ -1034,6 +1035,20 @@ void Filler::addManageChat() { }, &st::menuIconManage); } +void Filler::addBoostChat() { + if (const auto channel = _peer->asChannel()) { + const auto text = channel->isMegagroup() + ? tr::lng_boost_group_button(tr::now) + : tr::lng_boost_channel_button(tr::now); + const auto weak = base::make_weak(_controller); + _addAction(text, [=] { + if (const auto strong = weak.get()) { + strong->resolveBoostState(channel); + } + }, &st::menuIconBoosts); + } +} + void Filler::addViewStatistics() { if (const auto channel = _peer->asChannel()) { const auto controller = _controller; @@ -1058,12 +1073,6 @@ void Filler::addViewStatistics() { controller->showSection(Info::Boosts::Make(peer)); } }, &st::menuIconBoosts); - } else if (channel->isMegagroup()) { - _addAction(tr::lng_boost_group_button(tr::now), [=] { - if (const auto strong = weak.get()) { - controller->resolveBoostState(channel); - } - }, &st::menuIconBoosts); } if (canViewEarn) { _addAction(tr::lng_channel_earn_title(tr::now), [=] { @@ -1312,6 +1321,7 @@ void Filler::fillHistoryActions() { addStoryArchive(); addSupportInfo(); addManageChat(); + addBoostChat(); addCreatePoll(); addThemeEdit(); addViewDiscussion(); From 6fb1e23ed572a93d75f368bc2e598d6712b61dd6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 5 Apr 2024 18:12:20 +0400 Subject: [PATCH 48/71] Fix file dialog filter for videos from attach menu. --- Telegram/SourceFiles/history/history_widget.cpp | 2 +- .../SourceFiles/history/view/history_view_replies_section.cpp | 2 +- Telegram/SourceFiles/media/stories/media_stories_reply.cpp | 2 +- .../settings/business/settings_shortcut_messages.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 8d2dff310..a3bd3ebf8 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4496,7 +4496,7 @@ void HistoryWidget::chooseAttach( } const auto filter = (overrideSendImagesAsPhotos == true) - ? FileDialog::ImagesOrAllFilter() + ? FileDialog::PhotoVideoFilesFilter() : FileDialog::AllOrImagesFilter(); FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=]( diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 9451bd556..9543eeae0 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -867,7 +867,7 @@ void RepliesWidget::chooseAttach( } const auto filter = (overrideSendImagesAsPhotos == true) - ? FileDialog::ImagesOrAllFilter() + ? FileDialog::PhotoVideoFilesFilter() : FileDialog::AllOrImagesFilter(); FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=]( FileDialog::OpenResult &&result) { diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp index d6959d1a6..b1a33d9c1 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp @@ -431,7 +431,7 @@ void ReplyArea::chooseAttach( } const auto filter = (overrideSendImagesAsPhotos == true) - ? FileDialog::ImagesOrAllFilter() + ? FileDialog::PhotoVideoFilesFilter() : FileDialog::AllOrImagesFilter(); const auto weak = make_weak(&_shownPeerGuard); const auto callback = [=](FileDialog::OpenResult &&result) { diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index 9422c892c..efeffaac9 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -1428,7 +1428,7 @@ void ShortcutMessages::chooseAttach( _choosingAttach = false; const auto filter = (overrideSendImagesAsPhotos == true) - ? FileDialog::ImagesOrAllFilter() + ? FileDialog::PhotoVideoFilesFilter() : FileDialog::AllOrImagesFilter(); FileDialog::GetOpenPaths(this, tr::lng_choose_files(tr::now), filter, crl::guard(this, [=]( FileDialog::OpenResult &&result) { From f36e3c213eb81759a1f328a78b74ca21785e1f77 Mon Sep 17 00:00:00 2001 From: "Sergey A. Osokin" Date: Sat, 6 Apr 2024 12:23:16 -0400 Subject: [PATCH 49/71] Fix build on FreeBSD 13.3 / llvm 17.0.6 / qt 5.15.13 --- Telegram/SourceFiles/platform/linux/specific_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 7b390c29c..29b887941 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -510,7 +510,7 @@ QString SingleInstanceLocalServerName(const QString &hash) { #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) std::optional IsDarkMode() { - const auto result = base::Platform::XDP::ReadSetting( + auto result = base::Platform::XDP::ReadSetting( "org.freedesktop.appearance", "color-scheme"); From 7803f8e670b276db7a2a93c463b88f22e8b7ecec Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 6 Apr 2024 21:33:23 +0400 Subject: [PATCH 50/71] Simplify escaping when parsing IV. --- Telegram/SourceFiles/iv/iv_prepare.cpp | 67 ++++++++++++-------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/Telegram/SourceFiles/iv/iv_prepare.cpp b/Telegram/SourceFiles/iv/iv_prepare.cpp index ff21e4e55..6069d4407 100644 --- a/Telegram/SourceFiles/iv/iv_prepare.cpp +++ b/Telegram/SourceFiles/iv/iv_prepare.cpp @@ -51,7 +51,7 @@ template >> return Number(base::SafeRound(value * 10000.) / 100.); }; -[[nodiscard]] QByteArray EscapeAttr(QByteArray value) { +[[nodiscard]] QByteArray Escape(QByteArray value) { auto result = QByteArray(); result.reserve(value.size()); for (const auto &ch : value) { @@ -67,8 +67,8 @@ template >> return result; } -[[nodiscard]] QByteArray EscapeText(QByteArray value) { - return EscapeAttr(value); +[[nodiscard]] QByteArray Date(TimeId date) { + return Escape(langDateTimeFull(base::unixtime::parse(date)).toUtf8()); } class Parser final { @@ -391,9 +391,7 @@ QByteArray Parser::block(const MTPDpageBlockSubtitle &data) { QByteArray Parser::block(const MTPDpageBlockAuthorDate &data) { auto inner = rich(data.vauthor()); if (const auto date = data.vpublished_date().v) { - const auto parsed = base::unixtime::parse(date); - inner += " \xE2\x80\xA2 " - + tag("time", EscapeText(langDateTimeFull(parsed).toUtf8())); + inner += " \xE2\x80\xA2 " + tag("time", Date(date)); } return tag("address", inner); } @@ -412,7 +410,7 @@ QByteArray Parser::block(const MTPDpageBlockParagraph &data) { QByteArray Parser::block(const MTPDpageBlockPreformatted &data) { auto list = Attributes(); - const auto language = EscapeAttr(utf(data.vlanguage())); + const auto language = utf(data.vlanguage()); if (!language.isEmpty()) { list.push_back({ "data-language", language }); list.push_back({ "class", "lang-" + language }); @@ -430,7 +428,7 @@ QByteArray Parser::block(const MTPDpageBlockDivider &data) { } QByteArray Parser::block(const MTPDpageBlockAnchor &data) { - return tag("a", { { "name", EscapeAttr(utf(data.vname())) } }); + return tag("a", { { "name", utf(data.vname()) } }); } QByteArray Parser::block(const MTPDpageBlockList &data) { @@ -507,15 +505,13 @@ QByteArray Parser::block( }; auto result = tag("div", attributes, inner); - const auto href = data.vurl() - ? utf(*data.vurl()) - : photoFullUrl(photo); + const auto href = data.vurl() ? utf(*data.vurl()) : photoFullUrl(photo); const auto id = Number(photo.id); result = tag("a", { { "href", href }, { "oncontextmenu", data.vurl() ? QByteArray() : "return false;" }, { "data-context", data.vurl() ? QByteArray() : "viewer-photo" + id }, - }, result); + }, result); if (!slideshow) { result += caption(data.vcaption()); } @@ -621,9 +617,9 @@ QByteArray Parser::block(const MTPDpageBlockEmbed &data) { attributes.push_back({ "height", iframeHeight }); if (const auto url = data.vurl()) { if (!autosize) { - attributes.push_back({ "src", EscapeAttr(utf(url)) }); + attributes.push_back({ "src", utf(url) }); } else { - attributes.push_back({ "srcdoc", EscapeAttr(utf(url)) }); + attributes.push_back({ "srcdoc", utf(url) }); } } else if (const auto html = data.vhtml()) { attributes.push_back({ "src", embedUrl(html->v) }); @@ -661,12 +657,10 @@ QByteArray Parser::block(const MTPDpageBlockEmbedPost &data) { address += tag( "a", { { "rel", "author" }, { "onclick", "return false;" } }, - EscapeText(utf(data.vauthor()))); + utf(data.vauthor())); if (const auto date = data.vdate().v) { const auto parsed = base::unixtime::parse(date); - address += tag( - "time", - EscapeText(langDateTimeFull(parsed).toUtf8())); + address += tag("time", Date(date)); } const auto inner = tag("address", address) + list(data.vblocks()); result = tag("blockquote", { { "class", "embed-post" } }, inner); @@ -675,7 +669,7 @@ QByteArray Parser::block(const MTPDpageBlockEmbedPost &data) { const auto inner = tag("strong", utf(data.vauthor())) + tag( "small", - tag("a", { { "href", EscapeAttr(url) } }, EscapeText(url))); + tag("a", { { "href", url } }, url)); result = tag("section", { { "class", "embed-post" } }, inner); } result += caption(data.vcaption()); @@ -732,7 +726,7 @@ QByteArray Parser::block(const MTPDpageBlockChannel &data) { : "https://t.me/" + username; result = tag( "a", - { { "href", EscapeAttr(link) }, { "data-context", "channel" + id } }, + { { "href", link }, { "data-context", "channel" + id } }, result); _result.channelIds.emplace(id); return tag("section", { @@ -839,23 +833,21 @@ QByteArray Parser::block(const MTPDpageRelatedArticle &data) { inner += tag( "span", { { "class", "related-link-title" } }, - EscapeText(utf(*title))); + utf(*title)); } if (description) { inner += tag( "span", { { "class", "related-link-desc" } }, - EscapeText(utf(*description))); + utf(*description)); } if (author || published) { - const auto separator = (author && published) ? ", " : ""; - const auto parsed = base::unixtime::parse(published->v); inner += tag( "span", { { "class", "related-link-source" } }, - EscapeText((author ? utf(*author) : QByteArray()) - + separator - + langDateTimeFull(parsed).toUtf8())); + ((author ? utf(*author) : QByteArray()) + + ((author && published) ? ", " : QByteArray()) + + (published ? Date(published->v) : QByteArray()))); } result += tag("span", { { "class", "related-link-content" }, @@ -912,22 +904,25 @@ QByteArray Parser::block(const MTPDpageListItemBlocks &data) { } QByteArray Parser::block(const MTPDpageListOrderedItemText &data) { - return tag("li", { { "value", utf(data.vnum()) } }, rich(data.vtext())); + return tag( + "li", + { { "value", utf(data.vnum()) } }, + rich(data.vtext())); } QByteArray Parser::block(const MTPDpageListOrderedItemBlocks &data) { return tag( "li", - { { "value", EscapeAttr(utf(data.vnum())) } }, + { { "value", utf(data.vnum()) } }, list(data.vblocks())); } QByteArray Parser::utf(const MTPstring &text) { - return text.v; + return Escape(text.v); } QByteArray Parser::utf(const tl::conditional &text) { - return text ? text->v : QByteArray(); + return text ? utf(*text) : QByteArray(); } QByteArray Parser::tag( @@ -965,7 +960,7 @@ QByteArray Parser::rich(const MTPRichText &text) { { "\xE2\x81\xA8", "" }, { "\xE2\x81\xA9", "" }, }; - auto text = EscapeText(utf(data.vtext())); + auto text = utf(data.vtext()); for (const auto &[from, to] : replacements) { text.replace(from, to); } @@ -1016,8 +1011,8 @@ QByteArray Parser::rich(const MTPRichText &text) { }, rich(data.vtext())); }, [&](const MTPDtextEmail &data) { return tag("a", { - { "href", "mailto:" + EscapeAttr(utf(data.vemail())) }, - }, rich(data.vtext())); + { "href", "mailto:" + utf(data.vemail()) }, + }, rich(data.vtext())); }, [&](const MTPDtextSubscript &data) { return tag("sub", rich(data.vtext())); }, [&](const MTPDtextSuperscript &data) { @@ -1026,11 +1021,11 @@ QByteArray Parser::rich(const MTPRichText &text) { return tag("mark", rich(data.vtext())); }, [&](const MTPDtextPhone &data) { return tag("a", { - { "href", "tel:" + EscapeAttr(utf(data.vphone())) }, + { "href", "tel:" + utf(data.vphone()) }, }, rich(data.vtext())); }, [&](const MTPDtextAnchor &data) { const auto inner = rich(data.vtext()); - const auto name = EscapeAttr(utf(data.vname())); + const auto name = utf(data.vname()); return inner.isEmpty() ? tag("a", { { "name", name } }) : tag( From eaaa704fa458f0c6a9a2fcbf5607d865b0b857bc Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 6 Apr 2024 22:56:13 +0400 Subject: [PATCH 51/71] Check the URL in IV like in attach bots. --- Telegram/SourceFiles/iv/iv_instance.cpp | 10 +++++++++- .../SourceFiles/ui/chat/attach/attach_bot_webview.cpp | 7 +++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Telegram/SourceFiles/iv/iv_instance.cpp b/Telegram/SourceFiles/iv/iv_instance.cpp index 62527e35f..2362a6b61 100644 --- a/Telegram/SourceFiles/iv/iv_instance.cpp +++ b/Telegram/SourceFiles/iv/iv_instance.cpp @@ -838,6 +838,9 @@ void Instance::show( _shownSession = session; _shown->events() | rpl::start_with_next([=](Controller::Event event) { using Type = Controller::Event::Type; + const auto lower = event.url.toLower(); + const auto urlChecked = lower.startsWith("http://") + || lower.startsWith("https://"); switch (event.type) { case Type::Close: _shown = nullptr; @@ -852,7 +855,9 @@ void Instance::show( processJoinChannel(event.context); break; case Type::OpenLinkExternal: - File::OpenUrl(event.url); + if (urlChecked) { + File::OpenUrl(event.url); + } closeAll(); break; case Type::OpenMedia: @@ -891,6 +896,9 @@ void Instance::show( break; case Type::OpenPage: case Type::OpenLink: + if (!urlChecked) { + break; + } _shownSession->api().request(MTPmessages_GetWebPage( MTP_string(event.url), MTP_int(0) diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp index c9f3f93f8..262c852d5 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_bot_webview.cpp @@ -800,12 +800,11 @@ void Panel::openExternalLink(const QJsonObject &args) { _delegate->botClose(); return; } - const auto url = args["url"].toString(); const auto iv = args["try_instant_view"].toBool(); + const auto url = args["url"].toString(); const auto lower = url.toLower(); - if (url.isEmpty() - || (!lower.startsWith("http://") && !lower.startsWith("https://"))) { - LOG(("BotWebView Error: Bad 'url' in openExternalLink.")); + if (!lower.startsWith("http://") && !lower.startsWith("https://")) { + LOG(("BotWebView Error: Bad url in openExternalLink: %1").arg(url)); _delegate->botClose(); return; } else if (!allowOpenLink()) { From 1a2a1f1c17557466b661895e98d66dafad114b2f Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 6 Apr 2024 22:57:31 +0400 Subject: [PATCH 52/71] Version 4.16.4. - Bug fixes and other minor improvements. --- Telegram/Resources/uwp/AppX/AppxManifest.xml | 2 +- Telegram/Resources/winrc/Telegram.rc | 8 ++++---- Telegram/Resources/winrc/Updater.rc | 8 ++++---- Telegram/SourceFiles/core/version.h | 6 +++--- Telegram/build/version | 10 +++++----- changelog.txt | 4 ++++ 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 16a9f2e56..c4d83fefb 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="4.16.4.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 09de12abf..4d01d2396 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,3,0 - PRODUCTVERSION 4,16,3,0 + FILEVERSION 4,16,4,0 + PRODUCTVERSION 4,16,4,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.16.3.0" + VALUE "FileVersion", "4.16.4.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.3.0" + VALUE "ProductVersion", "4.16.4.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index f70a9486b..945b1bbb8 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,16,3,0 - PRODUCTVERSION 4,16,3,0 + FILEVERSION 4,16,4,0 + PRODUCTVERSION 4,16,4,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "4.16.3.0" + VALUE "FileVersion", "4.16.4.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.16.3.0" + VALUE "ProductVersion", "4.16.4.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 76e05eb4a..6b30124d7 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 = 4016003; -constexpr auto AppVersionStr = "4.16.3"; -constexpr auto AppBetaVersion = true; +constexpr auto AppVersion = 4016004; +constexpr auto AppVersionStr = "4.16.4"; +constexpr auto AppBetaVersion = false; constexpr auto AppAlphaVersion = TDESKTOP_ALPHA_VERSION; diff --git a/Telegram/build/version b/Telegram/build/version index b41f8cd69..7d28e3677 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -1,7 +1,7 @@ -AppVersion 4016003 +AppVersion 4016004 AppVersionStrMajor 4.16 -AppVersionStrSmall 4.16.3 -AppVersionStr 4.16.3 -BetaChannel 1 +AppVersionStrSmall 4.16.4 +AppVersionStr 4.16.4 +BetaChannel 0 AlphaVersion 0 -AppVersionOriginal 4.16.3.beta +AppVersionOriginal 4.16.4 diff --git a/changelog.txt b/changelog.txt index d792c1391..bff1b5e4f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +4.16.4 (06.04.24) + +- Bug fixes and other minor improvements. + 4.16.3 beta (05.04.24) - Improve media upload speed. From 77835a43a5269233aa6fa367ed7b8575ecb67f26 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 7 Apr 2024 11:19:23 +0400 Subject: [PATCH 53/71] Version 4.16.4: Update patches on Linux. --- Telegram/build/docker/centos_env/Dockerfile | 2 +- snap/snapcraft.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 2b5cf11cd..3a0b68392 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -51,7 +51,7 @@ FROM builder AS patches RUN git init patches \ && cd patches \ && git remote add origin {{ GIT }}/desktop-app/patches.git \ - && git fetch --depth=1 origin 174d6e51435e6eab0596904f9fba80a927ef4177 \ + && git fetch --depth=1 origin b8e22ca1511513024b22b5381153163ad3406a8c \ && git reset --hard FETCH_HEAD \ && rm -rf .git diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 1993f7997..574d3c996 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -168,7 +168,7 @@ parts: patches: source: https://github.com/desktop-app/patches.git source-depth: 1 - source-commit: 174d6e51435e6eab0596904f9fba80a927ef4177 + source-commit: b8e22ca1511513024b22b5381153163ad3406a8c plugin: dump override-pull: | craftctl default From 090fdfb4587f2bb9e20ed5925ac6842f83739062 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 3 Mar 2023 18:06:35 +0400 Subject: [PATCH 54/71] Add asan libraries to Docker image --- Telegram/build/docker/centos_env/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 3a0b68392..3aa48a91a 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -22,10 +22,10 @@ RUN dnf -y install epel-release \ python3.11-pip python3.11-devel gperf flex bison clang lld yasm \ file which perl-open perl-XML-Parser perl-IPC-Cmd xorg-x11-util-macros \ gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils \ - libffi-devel fontconfig-devel freetype-devel libX11-devel \ - alsa-lib-devel pulseaudio-libs-devel mesa-libGL-devel mesa-libEGL-devel \ - mesa-libgbm-devel libdrm-devel vulkan-devel libva-devel libvdpau-devel \ - glib2-devel at-spi2-core-devel gtk3-devel boost1.78-devel fmt-devel \ + gcc-toolset-12-libasan-devel libffi-devel fontconfig-devel freetype-devel \ + libX11-devel alsa-lib-devel pulseaudio-libs-devel mesa-libGL-devel \ + mesa-libEGL-devel mesa-libgbm-devel libdrm-devel vulkan-devel libva-devel \ + libvdpau-devel glib2-devel at-spi2-core-devel gtk3-devel boost1.78-devel fmt-devel \ && dnf clean all SHELL [ "bash", "-c", ". /opt/rh/gcc-toolset-12/enable; exec bash -c \"$@\"", "-s"] From 542153d950a6787c3bb457a53cf3786f62700dfa Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Fri, 5 Apr 2024 13:34:52 +0400 Subject: [PATCH 55/71] Use nasm from repository --- Telegram/build/docker/centos_env/Dockerfile | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Telegram/build/docker/centos_env/Dockerfile b/Telegram/build/docker/centos_env/Dockerfile index 3aa48a91a..9568cc7e4 100644 --- a/Telegram/build/docker/centos_env/Dockerfile +++ b/Telegram/build/docker/centos_env/Dockerfile @@ -19,7 +19,7 @@ ENV PKG_CONFIG_PATH /usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig:/usr/loc RUN dnf -y install epel-release \ && dnf config-manager --set-enabled powertools \ && dnf -y install autoconf automake libtool pkgconfig make patch git \ - python3.11-pip python3.11-devel gperf flex bison clang lld yasm \ + python3.11-pip python3.11-devel gperf flex bison clang lld nasm yasm \ file which perl-open perl-XML-Parser perl-IPC-Cmd xorg-x11-util-macros \ gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils \ gcc-toolset-12-libasan-devel libffi-devel fontconfig-devel freetype-devel \ @@ -55,17 +55,6 @@ RUN git init patches \ && git reset --hard FETCH_HEAD \ && rm -rf .git -FROM builder AS nasm -RUN git clone -b nasm-2.15.05 --depth=1 {{ GIT }}/netwide-assembler/nasm.git \ - && cd nasm \ - && ./autogen.sh \ - && ./configure \ - && make -j$(nproc) \ - && mkdir -p "{{ LibrariesPath }}/nasm-cache/usr/local/bin" \ - && install -c nasm "{{ LibrariesPath }}/nasm-cache/usr/local/bin/nasm" \ - && cd .. \ - && rm -rf nasm - FROM builder AS zlib RUN git init zlib \ && cd zlib \ @@ -166,8 +155,6 @@ RUN git clone -b v1.4 --depth=1 {{ GIT }}/xiph/opus.git \ && rm -rf opus FROM builder AS dav1d -COPY --link --from=nasm {{ LibrariesPath }}/nasm-cache / - RUN git clone -b 1.4.1 --depth=1 {{ GIT }}/videolan/dav1d.git \ && cd dav1d \ && meson build \ From 3da835152266630345e113eca6c482584aec337a Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 7 Apr 2024 13:31:34 +0400 Subject: [PATCH 56/71] Let Wayland to use QGuiApplication::screenAt (forgotten place) --- .../calls/group/ui/desktop_capture_choose_source.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/calls/group/ui/desktop_capture_choose_source.cpp b/Telegram/SourceFiles/calls/group/ui/desktop_capture_choose_source.cpp index ad59290c5..93c5ecf1e 100644 --- a/Telegram/SourceFiles/calls/group/ui/desktop_capture_choose_source.cpp +++ b/Telegram/SourceFiles/calls/group/ui/desktop_capture_choose_source.cpp @@ -587,11 +587,9 @@ void ChooseSourceProcess::setupGeometryWithParent( not_null parent) { _window->createWinId(); const auto parentScreen = [&] { - if (!::Platform::IsWayland()) { - if (const auto screen = QGuiApplication::screenAt( + if (const auto screen = QGuiApplication::screenAt( parent->geometry().center())) { - return screen; - } + return screen; } return parent->screen(); }(); From 84ce72ec7a7f39dddeea5c311a4ec1eb2776847b Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 7 Apr 2024 14:19:23 +0400 Subject: [PATCH 57/71] Fix a crash due to half-hidden media viewer --- Telegram/SourceFiles/core/file_utilities.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Telegram/SourceFiles/core/file_utilities.cpp b/Telegram/SourceFiles/core/file_utilities.cpp index 26cc17279..1de1e028f 100644 --- a/Telegram/SourceFiles/core/file_utilities.cpp +++ b/Telegram/SourceFiles/core/file_utilities.cpp @@ -159,7 +159,7 @@ void Launch(const QString &filepath) { void ShowInFolder(const QString &filepath) { crl::on_main([=] { Ui::PreventDelayedActivation(); - if (Platform::IsLinux()) { + if (Platform::IsX11()) { // Hide mediaview to make other apps visible. Core::App().hideMediaView(); } From 94f1d23788545edecafad311de029f29475ff02b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 8 Apr 2024 04:11:52 +0300 Subject: [PATCH 58/71] Fixed behavior to open section with scheduled messages at top. --- .../history/view/history_view_scheduled_section.cpp | 12 +++++++++--- .../history/view/history_view_scheduled_section.h | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 188d11338..6c989fd1e 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -58,11 +58,19 @@ namespace HistoryView { ScheduledMemento::ScheduledMemento(not_null history) : _history(history) , _forumTopic(nullptr) { + const auto list = _history->owner().scheduledMessages().list(_history); + if (!list.ids.empty()) { + _list.setScrollTopState({ .item = { .fullId = list.ids.front() } }); + } } ScheduledMemento::ScheduledMemento(not_null forumTopic) : _history(forumTopic->owningHistory()) , _forumTopic(forumTopic) { + const auto list = _history->owner().scheduledMessages().list(_forumTopic); + if (!list.ids.empty()) { + _list.setScrollTopState({ .item = { .fullId = list.ids.front() } }); + } } object_ptr ScheduledMemento::createWidget( @@ -1154,9 +1162,7 @@ Context ScheduledWidget::listContext() { } bool ScheduledWidget::listScrollTo(int top, bool syntetic) { - top = (top == ScrollMax && syntetic) - ? 0 - : std::clamp(top, 0, _scroll->scrollTopMax()); + top = std::clamp(top, 0, _scroll->scrollTopMax()); if (_scroll->scrollTop() == top) { updateInnerVisibleArea(); return false; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index 850e56bb7..43702688f 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -280,7 +280,7 @@ private: }; -class ScheduledMemento : public Window::SectionMemento { +class ScheduledMemento final : public Window::SectionMemento { public: ScheduledMemento(not_null history); ScheduledMemento(not_null forumTopic); From 47bf099b88ecb0ce02b1701de2e8b02b9ebeb73e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 8 Apr 2024 06:55:44 +0300 Subject: [PATCH 59/71] Fixed updating of field placeholder in compose controls while editing. --- .../history/view/controls/history_view_compose_controls.cpp | 1 + 1 file changed, 1 insertion(+) 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 185073db1..d47ee6ec1 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1973,6 +1973,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) { const auto guard = gsl::finally([&] { updateSendButtonType(); updateReplaceMediaButton(); + updateFieldPlaceholder(); updateControlsVisibility(); updateControlsGeometry(_wrap->size()); }); From 129b07c2c021e7a7c7d708809201cc59f9fc3a9c Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 8 Apr 2024 17:02:52 +0300 Subject: [PATCH 60/71] Improved style of box for all of user's own groups and channels. --- .../SourceFiles/window/window_main_menu.cpp | 105 ++++++++++++++++-- 1 file changed, 93 insertions(+), 12 deletions(-) diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index c49afd1a9..1f833a4d6 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "window/window_main_menu.h" +#include "apiwrap.h" #include "window/themes/window_theme.h" #include "window/window_peer_menu.h" #include "window/window_session_controller.h" @@ -24,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_options.h" #include "ui/new_badges.h" #include "ui/painter.h" +#include "ui/rect.h" #include "ui/vertical_list.h" #include "ui/unread_badge_paint.h" #include "inline_bots/bot_attach_web_view.h" @@ -45,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/premium_preview_box.h" #include "calls/calls_box_controller.h" #include "lang/lang_keys.h" +#include "lottie/lottie_icon.h" #include "core/click_handler_types.h" #include "core/application.h" #include "main/main_session.h" @@ -56,6 +59,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document_media.h" #include "data/data_folder.h" #include "data/data_session.h" +#include "data/data_document.h" +#include "data/data_file_origin.h" #include "data/data_user.h" #include "data/data_changes.h" #include "data/data_channel.h" @@ -116,10 +121,76 @@ not_null AddMyChannelsBox( bool chats) { button->setAcceptBoth(true); + const auto requestIcon = [=, session = &controller->session()]( + not_null box, + Fn)> done) { + const auto api = box->lifetime().make_state( + &session->mtp()); + api->request(MTPmessages_GetStickerSet( + Data::InputStickerSet({ + .shortName = u"tg_placeholders_android"_q, + }), + MTP_int(0) + )).done([=](const MTPmessages_StickerSet &result) { + result.match([&](const MTPDmessages_stickerSet &data) { + const auto &v = data.vdocuments().v; + if (v.size() > 1) { + done(session->data().processDocument(v[1])); + } + }, [](const MTPDmessages_stickerSetNotModified &) { + }); + }).send(); + }; + const auto addIcon = [=](not_null box) { + const auto widget = box->addRow(object_ptr(box)); + widget->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(widget); + p.setFont(st::boxTextFont); + p.setPen(st::windowSubTextFg); + p.drawText( + widget->rect(), + tr::lng_contacts_loading(tr::now), + style::al_center); + }, widget->lifetime()); + widget->resize(Size(st::maxStickerSize)); + widget->show(); + box->verticalLayout()->resizeToWidth(box->width()); + requestIcon(box, [=](not_null document) { + const auto view = document->createMediaView(); + const auto origin = document->stickerSetOrigin(); + controller->session().downloaderTaskFinished( + ) | rpl::take_while([=] { + if (view->bytes().isEmpty()) { + return true; + } + auto owned = Lottie::MakeIcon({ + .json = Images::UnpackGzip(view->bytes()), + .sizeOverride = Size(st::maxStickerSize), + }); + const auto icon = owned.get(); + widget->lifetime().add([kept = std::move(owned)]{}); + widget->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(widget); + icon->paint(p, (widget->width() - icon->width()) / 2, 0); + }, widget->lifetime()); + icon->animate( + [=] { widget->update(); }, + 0, + icon->framesCount()); + return false; + }) | rpl::start(widget->lifetime()); + view->automaticLoad(origin, nullptr); + view->videoThumbnailWanted(origin); + }); + }; + const auto myChannelsBox = [=](not_null box) { box->setTitle(chats ? tr::lng_notification_groups() : tr::lng_notification_channels()); + box->addButton(tr::lng_close(), [=] { box->closeBox(); }); const auto st = box->lifetime().make_state( st::defaultUserpicButton); @@ -133,17 +204,22 @@ not_null AddMyChannelsBox( void setPeer(not_null p) { const auto c = p->asChannel(); const auto g = p->asChat(); - _text.setText(st::defaultPeerListItem.nameStyle, p->name()); + _text.setText( + st::defaultPeerListItem.nameStyle, + ((c && c->isMegagroup()) ? u"[s] "_q : QString()) + + p->name()); const auto count = c ? c->membersCount() : g->count; _status.setText( st::defaultTextStyle, !p->username().isEmpty() ? ('@' + p->username()) - : count - ? tr::lng_chat_status_subscribers( - tr::now, - lt_count, - count) + : (count > 0) + ? ((c && !c->isMegagroup()) + ? tr::lng_chat_status_subscribers + : tr::lng_chat_status_members)( + tr::now, + lt_count, + count) : QString()); } @@ -192,15 +268,15 @@ not_null AddMyChannelsBox( }; const auto &data = controller->session().data(); + auto ids = std::vector(); if (chats) { - auto ids = std::vector(); data.enumerateGroups([&](not_null peer) { peer = peer->migrateToOrMe(); - const auto c = peer->asChannel(); - const auto g = peer->asChat(); if (ranges::contains(ids, peer->id)) { return; } + const auto c = peer->asChannel(); + const auto g = peer->asChat(); if ((c && c->amCreator()) || (g && g->amCreator())) { ids.push_back(peer->id); add(peer); @@ -208,11 +284,16 @@ not_null AddMyChannelsBox( }); } else { data.enumerateBroadcasts([&](not_null channel) { - if (channel->amCreator()) { + if (channel->amCreator() + && !ranges::contains(ids, channel->id)) { + ids.push_back(channel->id); add(channel); } }); } + if (ids.empty()) { + addIcon(box); + } }; using Menu = base::unique_qptr; @@ -224,12 +305,12 @@ not_null AddMyChannelsBox( (*menu) = base::make_unique_q( button, - st::defaultPopupMenu); + st::popupMenuWithIcons); (*menu)->addAction( (chats ? tr::lng_menu_my_groups : tr::lng_menu_my_channels)( tr::now), [=] { controller->uiShow()->showBox(Box(myChannelsBox)); }, - nullptr); + chats ? &st::menuIconGroups : &st::menuIconChannel); (*menu)->popup(QCursor::pos()); }); From c5febce548f814e4526849f15ae79ac9aed4bcdb Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 8 Apr 2024 17:34:58 +0300 Subject: [PATCH 61/71] Moved out some functions for main menu to separated files. --- Telegram/CMakeLists.txt | 2 + .../SourceFiles/window/window_main_menu.cpp | 417 ++---------------- .../window/window_main_menu_helpers.cpp | 368 ++++++++++++++++ .../window/window_main_menu_helpers.h | 33 ++ 4 files changed, 446 insertions(+), 374 deletions(-) create mode 100644 Telegram/SourceFiles/window/window_main_menu_helpers.cpp create mode 100644 Telegram/SourceFiles/window/window_main_menu_helpers.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 4988f34b3..2e26bf4d6 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1503,6 +1503,8 @@ PRIVATE window/window_lock_widgets.h window/window_main_menu.cpp window/window_main_menu.h + window/window_main_menu_helpers.cpp + window/window_main_menu_helpers.h window/window_media_preview.cpp window/window_media_preview.h window/window_peer_menu.cpp diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 1f833a4d6..bf1b21962 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -8,70 +8,60 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_main_menu.h" #include "apiwrap.h" -#include "window/themes/window_theme.h" -#include "window/window_peer_menu.h" -#include "window/window_session_controller.h" -#include "window/window_controller.h" +#include "base/qt_signal_producer.h" +#include "boxes/about_box.h" +#include "boxes/peer_list_controllers.h" +#include "boxes/premium_preview_box.h" +#include "calls/calls_box_controller.h" +#include "core/application.h" +#include "core/click_handler_types.h" +#include "data/data_changes.h" +#include "data/data_document_media.h" +#include "data/data_folder.h" +#include "data/data_session.h" +#include "data/data_stories.h" +#include "data/data_user.h" +#include "info/info_memento.h" +#include "info/profile/info_profile_badge.h" +#include "info/profile/info_profile_emoji_status_panel.h" +#include "info/stories/info_stories_widget.h" +#include "lang/lang_keys.h" +#include "main/main_account.h" +#include "main/main_domain.h" +#include "main/main_session.h" +#include "main/main_session_settings.h" +#include "mtproto/mtproto_config.h" +#include "settings/settings_advanced.h" +#include "settings/settings_calls.h" +#include "settings/settings_information.h" +#include "storage/localstorage.h" +#include "storage/storage_account.h" +#include "support/support_templates.h" +#include "ui/boxes/confirm_box.h" #include "ui/chat/chat_theme.h" #include "ui/controls/userpic_button.h" #include "ui/effects/snowflakes.h" #include "ui/effects/toggle_arrow.h" +#include "ui/painter.h" +#include "ui/text/text_options.h" +#include "ui/text/text_utilities.h" +#include "ui/unread_badge_paint.h" +#include "ui/vertical_list.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" -#include "ui/widgets/tooltip.h" #include "ui/wrap/slide_wrap.h" -#include "ui/text/text_utilities.h" -#include "ui/text/text_options.h" -#include "ui/new_badges.h" -#include "ui/painter.h" -#include "ui/rect.h" -#include "ui/vertical_list.h" -#include "ui/unread_badge_paint.h" -#include "inline_bots/bot_attach_web_view.h" -#include "storage/localstorage.h" -#include "storage/storage_account.h" -#include "support/support_templates.h" -#include "settings/settings_advanced.h" -#include "settings/settings_calls.h" -#include "settings/settings_information.h" -#include "info/profile/info_profile_badge.h" -#include "info/profile/info_profile_emoji_status_panel.h" -#include "info/stories/info_stories_widget.h" -#include "info/info_memento.h" -#include "base/platform/base_platform_info.h" -#include "base/qt_signal_producer.h" -#include "boxes/about_box.h" -#include "ui/boxes/confirm_box.h" -#include "boxes/peer_list_controllers.h" -#include "boxes/premium_preview_box.h" -#include "calls/calls_box_controller.h" -#include "lang/lang_keys.h" -#include "lottie/lottie_icon.h" -#include "core/click_handler_types.h" -#include "core/application.h" -#include "main/main_session.h" -#include "main/main_session_settings.h" -#include "main/main_account.h" -#include "main/main_domain.h" -#include "mtproto/mtproto_config.h" -#include "data/data_chat.h" -#include "data/data_document_media.h" -#include "data/data_folder.h" -#include "data/data_session.h" -#include "data/data_document.h" -#include "data/data_file_origin.h" -#include "data/data_user.h" -#include "data/data_changes.h" -#include "data/data_channel.h" -#include "data/data_stories.h" -#include "mainwidget.h" +#include "window/themes/window_theme.h" +#include "window/window_controller.h" +#include "window/window_main_menu_helpers.h" +#include "window/window_peer_menu.h" +#include "window/window_session_controller.h" #include "styles/style_chat.h" // popupMenuExpandedSeparator -#include "styles/style_window.h" -#include "styles/style_settings.h" #include "styles/style_info.h" // infoTopBarMenu #include "styles/style_layers.h" #include "styles/style_menu_icons.h" +#include "styles/style_settings.h" +#include "styles/style_window.h" #include #include @@ -84,239 +74,6 @@ namespace { constexpr auto kPlayStatusLimit = 2; -class VersionLabel final - : public Ui::FlatLabel - , public Ui::AbstractTooltipShower { -public: - using Ui::FlatLabel::FlatLabel; - - void clickHandlerActiveChanged( - const ClickHandlerPtr &action, - bool active) override { - update(); - if (active && action && !action->dragText().isEmpty()) { - Ui::Tooltip::Show(1000, this); - } else { - Ui::Tooltip::Hide(); - } - } - - QString tooltipText() const override { - return u"Build date: %1."_q.arg(__DATE__); - } - - QPoint tooltipPos() const override { - return QCursor::pos(); - } - - bool tooltipWindowActive() const override { - return Ui::AppInFocus() && Ui::InFocusChain(window()); - } - -}; - -not_null AddMyChannelsBox( - not_null button, - not_null controller, - bool chats) { - button->setAcceptBoth(true); - - const auto requestIcon = [=, session = &controller->session()]( - not_null box, - Fn)> done) { - const auto api = box->lifetime().make_state( - &session->mtp()); - api->request(MTPmessages_GetStickerSet( - Data::InputStickerSet({ - .shortName = u"tg_placeholders_android"_q, - }), - MTP_int(0) - )).done([=](const MTPmessages_StickerSet &result) { - result.match([&](const MTPDmessages_stickerSet &data) { - const auto &v = data.vdocuments().v; - if (v.size() > 1) { - done(session->data().processDocument(v[1])); - } - }, [](const MTPDmessages_stickerSetNotModified &) { - }); - }).send(); - }; - const auto addIcon = [=](not_null box) { - const auto widget = box->addRow(object_ptr(box)); - widget->paintRequest( - ) | rpl::start_with_next([=] { - auto p = QPainter(widget); - p.setFont(st::boxTextFont); - p.setPen(st::windowSubTextFg); - p.drawText( - widget->rect(), - tr::lng_contacts_loading(tr::now), - style::al_center); - }, widget->lifetime()); - widget->resize(Size(st::maxStickerSize)); - widget->show(); - box->verticalLayout()->resizeToWidth(box->width()); - requestIcon(box, [=](not_null document) { - const auto view = document->createMediaView(); - const auto origin = document->stickerSetOrigin(); - controller->session().downloaderTaskFinished( - ) | rpl::take_while([=] { - if (view->bytes().isEmpty()) { - return true; - } - auto owned = Lottie::MakeIcon({ - .json = Images::UnpackGzip(view->bytes()), - .sizeOverride = Size(st::maxStickerSize), - }); - const auto icon = owned.get(); - widget->lifetime().add([kept = std::move(owned)]{}); - widget->paintRequest( - ) | rpl::start_with_next([=] { - auto p = QPainter(widget); - icon->paint(p, (widget->width() - icon->width()) / 2, 0); - }, widget->lifetime()); - icon->animate( - [=] { widget->update(); }, - 0, - icon->framesCount()); - return false; - }) | rpl::start(widget->lifetime()); - view->automaticLoad(origin, nullptr); - view->videoThumbnailWanted(origin); - }); - }; - - const auto myChannelsBox = [=](not_null box) { - box->setTitle(chats - ? tr::lng_notification_groups() - : tr::lng_notification_channels()); - box->addButton(tr::lng_close(), [=] { box->closeBox(); }); - - const auto st = box->lifetime().make_state( - st::defaultUserpicButton); - st->photoSize = st::defaultPeerListItem.photoSize; - st->size = QSize(st->photoSize, st->photoSize); - - class Button final : public Ui::SettingsButton { - public: - using Ui::SettingsButton::SettingsButton; - - void setPeer(not_null p) { - const auto c = p->asChannel(); - const auto g = p->asChat(); - _text.setText( - st::defaultPeerListItem.nameStyle, - ((c && c->isMegagroup()) ? u"[s] "_q : QString()) - + p->name()); - const auto count = c ? c->membersCount() : g->count; - _status.setText( - st::defaultTextStyle, - !p->username().isEmpty() - ? ('@' + p->username()) - : (count > 0) - ? ((c && !c->isMegagroup()) - ? tr::lng_chat_status_subscribers - : tr::lng_chat_status_members)( - tr::now, - lt_count, - count) - : QString()); - } - - int resizeGetHeight(int) override { - return st::defaultPeerListItem.height; - } - - void paintEvent(QPaintEvent *e) override { - Ui::SettingsButton::paintEvent(e); - auto p = Painter(this); - const auto &st = st::defaultPeerListItem; - const auto availableWidth = width() - - st::boxRowPadding.right() - - st.namePosition.x(); - p.setPen(st.nameFg); - auto context = Ui::Text::PaintContext{ - .position = st.namePosition, - .outerWidth = availableWidth, - .availableWidth = availableWidth, - .elisionLines = 1, - }; - _text.draw(p, context); - p.setPen(st.statusFg); - context.position = st.statusPosition; - _status.draw(p, context); - } - - private: - Ui::Text::String _text; - Ui::Text::String _status; - - }; - - const auto add = [&](not_null peer) { - const auto row = box->addRow( - object_ptr