From e89c95551ff50b47b050f89f60465ffdbd6e57b4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 27 Feb 2022 14:14:39 +0300 Subject: [PATCH] Show correct downloads elements context menu. --- .../calls/group/calls_group_call.cpp | 4 +- .../data/data_download_manager.cpp | 67 +++++++ .../SourceFiles/data/data_download_manager.h | 2 + Telegram/SourceFiles/history/history_item.cpp | 16 +- Telegram/SourceFiles/history/history_item.h | 1 + .../downloads/info_downloads_inner_widget.cpp | 4 +- .../downloads/info_downloads_inner_widget.h | 3 +- .../downloads/info_downloads_provider.cpp | 22 ++- .../info/downloads/info_downloads_provider.h | 8 + .../info/downloads/info_downloads_widget.cpp | 8 + .../info/downloads/info_downloads_widget.h | 3 + .../SourceFiles/info/info_content_widget.h | 2 +- Telegram/SourceFiles/info/info_controller.cpp | 2 +- Telegram/SourceFiles/info/info_top_bar.cpp | 67 +++---- Telegram/SourceFiles/info/info_top_bar.h | 7 +- .../SourceFiles/info/info_wrap_widget.cpp | 6 +- Telegram/SourceFiles/info/info_wrap_widget.h | 5 + .../info/media/info_media_common.cpp | 11 +- .../info/media/info_media_common.h | 18 +- .../info/media/info_media_inner_widget.cpp | 4 +- .../info/media/info_media_inner_widget.h | 2 +- .../info/media/info_media_list_widget.cpp | 165 ++++++++++++------ .../info/media/info_media_list_widget.h | 14 +- .../info/media/info_media_provider.cpp | 24 ++- .../info/media/info_media_provider.h | 8 + .../info/media/info_media_widget.cpp | 4 +- .../info/media/info_media_widget.h | 2 +- .../polls/info_polls_results_inner_widget.cpp | 2 +- .../SourceFiles/overview/overview_layout.cpp | 6 +- .../delete_message_context_action.cpp | 9 +- .../SourceFiles/window/window_peer_menu.cpp | 6 +- .../SourceFiles/window/window_peer_menu.h | 7 +- 32 files changed, 362 insertions(+), 147 deletions(-) diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index d3bcdcc287..ba50719bb5 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -2601,7 +2601,9 @@ void GroupCall::requestCurrentTimeStart( }); }).fail([=] { finish(0); - }).handleAllErrors().send(); + }).handleAllErrors().toDC( + MTP::groupCallStreamDcId(_broadcastDcId) + ).send(); } void GroupCall::requestCurrentTimeCancel( diff --git a/Telegram/SourceFiles/data/data_download_manager.cpp b/Telegram/SourceFiles/data/data_download_manager.cpp index c83b16c1be..655c787c1f 100644 --- a/Telegram/SourceFiles/data/data_download_manager.cpp +++ b/Telegram/SourceFiles/data/data_download_manager.cpp @@ -242,6 +242,73 @@ void DownloadManager::addLoaded( } } +void DownloadManager::deleteFiles(const std::vector &ids) { + struct DocumentDescriptor { + uint64 sessionUniqueId = 0; + DocumentId documentId = 0; + FullMsgId itemId; + }; + auto sessions = base::flat_set>(); + auto files = base::flat_map(); + for (const auto &id : ids) { + if (const auto item = MessageByGlobalId(id)) { + const auto session = &item->history()->session(); + const auto i = _sessions.find(session); + if (i == end(_sessions)) { + continue; + } + auto &data = i->second; + const auto j = ranges::find( + data.downloading, + not_null{ item }, + ByItem); + if (j != end(data.downloading)) { + cancel(data, j); + } + + const auto k = ranges::find(data.downloaded, item, ByItem); + if (k != end(data.downloaded)) { + const auto document = k->object->document; + files.emplace(k->path, DocumentDescriptor{ + .sessionUniqueId = id.sessionUniqueId, + .documentId = document ? document->id : DocumentId(), + .itemId = id.itemId, + }); + _loaded.remove(item); + _generated.remove(item); + if (document) { + _generatedDocuments.remove(document); + } + data.downloaded.erase(k); + _loadedRemoved.fire_copy(item); + + sessions.emplace(session); + } + } + } + for (const auto session : sessions) { + writePostponed(session); + } + crl::async([files = std::move(files)] { + for (const auto &[path, descriptor] : files) { + QFile(path).remove(); + crl::on_main([descriptor] { + if (const auto session = SessionByUniqueId( + descriptor.sessionUniqueId)) { + if (const auto id = descriptor.documentId) { + [[maybe_unused]] const auto location + = session->data().document(id)->location(true); + } + const auto itemId = descriptor.itemId; + if (const auto item = session->data().message(itemId)) { + session->data().requestItemRepaint(item); + } + } + }); + } + }); +} + auto DownloadManager::loadingList() const -> ranges::any_view { return ranges::views::all( diff --git a/Telegram/SourceFiles/data/data_download_manager.h b/Telegram/SourceFiles/data/data_download_manager.h index ae99be5472..532d6b3582 100644 --- a/Telegram/SourceFiles/data/data_download_manager.h +++ b/Telegram/SourceFiles/data/data_download_manager.h @@ -87,6 +87,8 @@ public: const QString &path, DownloadDate started); + void deleteFiles(const std::vector &ids); + [[nodiscard]] auto loadingList() const -> ranges::any_view; [[nodiscard]] DownloadProgress loadingProgress() const; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index b1509aa88c..05a46a5b80 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -1308,20 +1308,28 @@ HistoryItem::~HistoryItem() { applyTTL(0); } -HistoryItem *MessageByGlobalId(GlobalMsgId globalId) { - if (!globalId.sessionUniqueId || !globalId.itemId) { +Main::Session *SessionByUniqueId(uint64 sessionUniqueId) { + if (!sessionUniqueId) { return nullptr; } for (const auto &[index, account] : Core::App().domain().accounts()) { if (const auto session = account->maybeSession()) { - if (session->uniqueId() == globalId.sessionUniqueId) { - return session->data().message(globalId.itemId); + if (session->uniqueId() == sessionUniqueId) { + return session; } } } return nullptr; } +HistoryItem *MessageByGlobalId(GlobalMsgId globalId) { + const auto sessionId = globalId.itemId ? globalId.sessionUniqueId : 0; + if (const auto session = SessionByUniqueId(sessionId)) { + return session->data().message(globalId.itemId); + } + return nullptr; +} + QDateTime ItemDateTime(not_null item) { return base::unixtime::parse(item->date()); } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 8dd89778d8..98f9430f6c 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -495,6 +495,7 @@ private: }; +[[nodiscard]] Main::Session *SessionByUniqueId(uint64 sessionUniqueId); [[nodiscard]] HistoryItem *MessageByGlobalId(GlobalMsgId globalId); [[nodiscard]] QDateTime ItemDateTime(not_null item); diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.cpp b/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.cpp index 33c6d665b5..38aba69a4d 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.cpp +++ b/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.cpp @@ -148,8 +148,8 @@ rpl::producer InnerWidget::selectedListValue() const { ) | rpl::flatten_latest(); } -void InnerWidget::cancelSelection() { - _list->cancelSelection(); +void InnerWidget::selectionAction(SelectionAction action) { + _list->selectionAction(action); } InnerWidget::~InnerWidget() = default; diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.h b/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.h index 90d3a2b692..2d2047f5e4 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.h +++ b/Telegram/SourceFiles/info/downloads/info_downloads_inner_widget.h @@ -21,6 +21,7 @@ namespace Info { class Controller; struct SelectedItems; +enum class SelectionAction; namespace Media { class ListWidget; @@ -46,7 +47,7 @@ public: rpl::producer scrollToRequests() const; rpl::producer selectedListValue() const; - void cancelSelection(); + void selectionAction(SelectionAction action); ~InnerWidget(); diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp b/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp index d71c693692..9332411de5 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp +++ b/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp @@ -335,13 +335,33 @@ std::unique_ptr Provider::createLayout( return nullptr; } +ListItemSelectionData Provider::computeSelectionData( + not_null item, + TextSelection selection) { + auto result = ListItemSelectionData(selection); + result.canDelete = true; + result.canForward = item->allowsForward() + && (&item->history()->session() == &_controller->session()); + return result; +} + void Provider::applyDragSelection( ListSelectedMap &selected, not_null fromItem, bool skipFrom, not_null tillItem, bool skipTill) { - // #TODO downloads + // #TODO downloads selection +} + +bool Provider::allowSaveFileAs( + not_null item, + not_null document) { + return false; +} + +std::optional Provider::deleteMenuPhrase() { + return u"Delete from disk"_q; } void Provider::saveState( diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_provider.h b/Telegram/SourceFiles/info/downloads/info_downloads_provider.h index f1bdf8471a..919ebfe712 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_provider.h +++ b/Telegram/SourceFiles/info/downloads/info_downloads_provider.h @@ -53,6 +53,9 @@ public: not_null a, not_null b) override; + Media::ListItemSelectionData computeSelectionData( + not_null item, + TextSelection selection) override; void applyDragSelection( Media::ListSelectedMap &selected, not_null fromItem, @@ -60,6 +63,11 @@ public: not_null tillItem, bool skipTill) override; + bool allowSaveFileAs( + not_null item, + not_null document) override; + std::optional deleteMenuPhrase() override; + void saveState( not_null memento, Media::ListScrollTopState scrollState) override; diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp b/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp index 33542f4b87..20c7517502 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp +++ b/Telegram/SourceFiles/info/downloads/info_downloads_widget.cpp @@ -93,6 +93,14 @@ void Widget::restoreState(not_null memento) { scrollTopRestore(memento->scrollTop()); } +rpl::producer Widget::selectedListValue() const { + return _inner->selectedListValue(); +} + +void Widget::selectionAction(SelectionAction action) { + _inner->selectionAction(action); +} + std::shared_ptr Make(not_null self) { return std::make_shared( std::vector>( diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_widget.h b/Telegram/SourceFiles/info/downloads/info_downloads_widget.h index ce848cf4ef..98e5234835 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_widget.h +++ b/Telegram/SourceFiles/info/downloads/info_downloads_widget.h @@ -54,6 +54,9 @@ public: const QRect &geometry, not_null memento); + rpl::producer selectedListValue() const override; + void selectionAction(SelectionAction action) override; + private: void saveState(not_null memento); void restoreState(not_null memento); diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index 548df85fb8..35b67f39ed 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -70,7 +70,7 @@ public: QRect floatPlayerAvailableRect() const; virtual rpl::producer selectedListValue() const; - virtual void cancelSelection() { + virtual void selectionAction(SelectionAction action) { } virtual void saveChanges(FnMut done); diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp index 0caaf17f02..0b339f8452 100644 --- a/Telegram/SourceFiles/info/info_controller.cpp +++ b/Telegram/SourceFiles/info/info_controller.cpp @@ -303,7 +303,7 @@ rpl::producer Controller::searchEnabledByContent() const { rpl::producer Controller::mediaSourceQueryValue() const { return _searchController ? _searchController->currentQueryValue() - : rpl::single(QString()); // #TODO downloads + : rpl::single(QString()); // #TODO downloads search } rpl::producer Controller::mediaSource( diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp index 7e040ba82e..f87e12c41d 100644 --- a/Telegram/SourceFiles/info/info_top_bar.cpp +++ b/Telegram/SourceFiles/info/info_top_bar.cpp @@ -434,8 +434,8 @@ SelectedItems TopBar::takeSelectedItems() { return std::move(_selectedItems); } -rpl::producer<> TopBar::cancelSelectionRequests() const { - return _cancelSelectionClicks.events(); +rpl::producer TopBar::selectionActionRequests() const { + return _selectionActionRequests.events(); } void TopBar::updateSelectionState() { @@ -466,9 +466,10 @@ void TopBar::createSelectionControls() { st::infoTopBarScale)); _cancelSelection->setDuration(st::infoTopBarDuration); _cancelSelection->entity()->clicks( - ) | rpl::to_empty - | rpl::start_to_stream( - _cancelSelectionClicks, + ) | rpl::map_to( + SelectionAction::Clear + ) | rpl::start_to_stream( + _selectionActionRequests, _cancelSelection->lifetime()); _selectionText = wrap(Ui::CreateChild>( this, @@ -488,7 +489,12 @@ void TopBar::createSelectionControls() { _forward.data(), [this] { return selectionMode() && _canForward; }); _forward->setDuration(st::infoTopBarDuration); - _forward->entity()->addClickHandler([this] { performForward(); }); + _forward->entity()->clicks( + ) | rpl::map_to( + SelectionAction::Forward + ) | rpl::start_to_stream( + _selectionActionRequests, + _cancelSelection->lifetime()); _forward->entity()->setVisible(_canForward); _delete = wrap(Ui::CreateChild>( this, @@ -498,7 +504,12 @@ void TopBar::createSelectionControls() { _delete.data(), [this] { return selectionMode() && _canDelete; }); _delete->setDuration(st::infoTopBarDuration); - _delete->entity()->addClickHandler([this] { performDelete(); }); + _delete->entity()->clicks( + ) | rpl::map_to( + SelectionAction::Delete + ) | rpl::start_to_stream( + _selectionActionRequests, + _cancelSelection->lifetime()); _delete->entity()->setVisible(_canDelete); updateControlsGeometry(width()); @@ -541,50 +552,12 @@ bool TopBar::searchMode() const { return _searchModeAvailable && _searchModeEnabled; } -MessageIdsList TopBar::collectItems() const { - return ranges::views::all( - _selectedItems.list - ) | ranges::views::transform([](auto &&item) { - return item.globalId; - }) | ranges::views::filter([&](const GlobalMsgId &globalId) { - const auto session = &_navigation->session(); - return (globalId.sessionUniqueId == session->uniqueId()) - && (session->data().message(globalId.itemId) != nullptr); - }) | ranges::views::transform([](const GlobalMsgId &globalId) { - return globalId.itemId; - }) | ranges::to_vector; -} - void TopBar::performForward() { - auto items = collectItems(); - if (items.empty()) { - _cancelSelectionClicks.fire({}); - return; - } - Window::ShowForwardMessagesBox( - _navigation, - std::move(items), - [weak = Ui::MakeWeak(this)] { - if (weak) { - weak->_cancelSelectionClicks.fire({}); - } - }); + _selectionActionRequests.fire(SelectionAction::Forward); } void TopBar::performDelete() { - // #TODO downloads - auto items = collectItems(); - if (items.empty()) { - _cancelSelectionClicks.fire({}); - } else { - auto box = Box( - &_navigation->session(), - std::move(items)); - box->setDeleteConfirmedCallback(crl::guard(this, [=] { - _cancelSelectionClicks.fire({}); - })); - _navigation->parentController()->show(std::move(box)); - } + _selectionActionRequests.fire(SelectionAction::Delete); } rpl::producer TitleValue( diff --git a/Telegram/SourceFiles/info/info_top_bar.h b/Telegram/SourceFiles/info/info_top_bar.h index 4cb3035fb2..caa3793b58 100644 --- a/Telegram/SourceFiles/info/info_top_bar.h +++ b/Telegram/SourceFiles/info/info_top_bar.h @@ -83,7 +83,8 @@ public: void setSelectedItems(SelectedItems &&items); SelectedItems takeSelectedItems(); - rpl::producer<> cancelSelectionRequests() const; + [[nodiscard]] auto selectionActionRequests() const + -> rpl::producer; void finishAnimating() { updateControlsVisibility(anim::type::instant); @@ -115,9 +116,7 @@ private: [[nodiscard]] bool computeCanForward() const; void updateSelectionState(); void createSelectionControls(); - void clearSelectionControls(); - MessageIdsList collectItems() const; void performForward(); void performDelete(); @@ -161,7 +160,7 @@ private: QPointer> _selectionText; QPointer> _forward; QPointer> _delete; - rpl::event_stream<> _cancelSelectionClicks; + rpl::event_stream _selectionActionRequests; using UpdateCallback = Fn; std::map _updateControlCallbacks; diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 1f4749edd9..28aebfa9cb 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -343,9 +343,9 @@ void WrapWidget::createTopBar() { _controller.get(), TopBarStyle(wrapValue), std::move(selectedItems)); - _topBar->cancelSelectionRequests( - ) | rpl::start_with_next([this] { - _content->cancelSelection(); + _topBar->selectionActionRequests( + ) | rpl::start_with_next([=](SelectionAction action) { + _content->selectionAction(action); }, _topBar->lifetime()); _topBar->setTitle(TitleValue( diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h index 63ab1e9488..37e23012e7 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.h +++ b/Telegram/SourceFiles/info/info_wrap_widget.h @@ -66,7 +66,12 @@ struct SelectedItems { Storage::SharedMediaType type; std::vector list; +}; +enum class SelectionAction { + Clear, + Forward, + Delete, }; class WrapWidget final : public Window::SectionWidget { diff --git a/Telegram/SourceFiles/info/media/info_media_common.cpp b/Telegram/SourceFiles/info/media/info_media_common.cpp index bf7849a9d5..fde5dd01bb 100644 --- a/Telegram/SourceFiles/info/media/info_media_common.cpp +++ b/Telegram/SourceFiles/info/media/info_media_common.cpp @@ -28,22 +28,19 @@ UniversalMsgId GetUniversalId(not_null layout) { bool ChangeItemSelection( ListSelectedMap &selected, not_null item, - TextSelection selection) { + ListItemSelectionData selectionData) { const auto changeExisting = [&](auto it) { if (it == selected.cend()) { return false; - } else if (it->second.text != selection) { - it->second.text = selection; + } else if (it->second != selectionData) { + it->second = selectionData; return true; } return false; }; if (selected.size() < MaxSelectedItems) { - const auto [i, ok] = selected.try_emplace(item, selection); + const auto [i, ok] = selected.try_emplace(item, selectionData); if (ok) { - // #TODO downloads - i->second.canDelete = item->canDelete(); - i->second.canForward = item->allowsForward(); return true; } return changeExisting(i); diff --git a/Telegram/SourceFiles/info/media/info_media_common.h b/Telegram/SourceFiles/info/media/info_media_common.h index d55b555b97..689abc20bf 100644 --- a/Telegram/SourceFiles/info/media/info_media_common.h +++ b/Telegram/SourceFiles/info/media/info_media_common.h @@ -32,6 +32,14 @@ struct ListItemSelectionData { bool canForward = false; }; +inline bool operator==( + ListItemSelectionData a, + ListItemSelectionData b) { + return (a.text == b.text) + && (a.canDelete == b.canDelete) + && (a.canForward == b.canForward); +} + using ListSelectedMap = base::flat_map< not_null, ListItemSelectionData, @@ -83,7 +91,7 @@ using UniversalMsgId = MsgId; bool ChangeItemSelection( ListSelectedMap &selected, not_null item, - TextSelection selection); + ListItemSelectionData selectionData); class ListSectionDelegate { public: @@ -132,6 +140,9 @@ public: not_null a, not_null b) = 0; + [[nodiscard]] virtual ListItemSelectionData computeSelectionData( + not_null item, + TextSelection selection) = 0; virtual void applyDragSelection( ListSelectedMap &selected, not_null fromItem, @@ -139,6 +150,11 @@ public: not_null tillItem, bool skipTill) = 0; + [[nodiscard]] virtual bool allowSaveFileAs( + not_null item, + not_null document) = 0; + [[nodiscard]] virtual std::optional deleteMenuPhrase() = 0; + virtual void saveState( not_null memento, ListScrollTopState scrollState) = 0; diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp index 96db45a632..63fcec862c 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp @@ -281,8 +281,8 @@ rpl::producer InnerWidget::selectedListValue() const { ) | rpl::flatten_latest(); } -void InnerWidget::cancelSelection() { - _list->cancelSelection(); +void InnerWidget::selectionAction(SelectionAction action) { + _list->selectionAction(action); } InnerWidget::~InnerWidget() = default; diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.h b/Telegram/SourceFiles/info/media/info_media_inner_widget.h index af5a4bd2cb..1b4d55276d 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.h @@ -48,7 +48,7 @@ public: rpl::producer scrollToRequests() const; rpl::producer selectedListValue() const; - void cancelSelection(); + void selectionAction(SelectionAction action); ~InnerWidget(); diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 2df91305d7..488c457970 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_file_click_handler.h" #include "data/data_file_origin.h" +#include "data/data_download_manager.h" #include "history/history_item.h" #include "history/history.h" #include "history/view/history_view_cursor_state.h" @@ -30,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "window/window_peer_menu.h" #include "ui/widgets/popup_menu.h" +#include "ui/boxes/confirm_box.h" #include "ui/controls/delete_message_context_action.h" #include "ui/chat/chat_style.h" #include "ui/cached_round_corners.h" @@ -46,9 +48,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/delete_messages_box.h" #include "boxes/peer_list_controllers.h" #include "core/file_utilities.h" +#include "core/application.h" #include "facades.h" #include "styles/style_overview.h" #include "styles/style_info.h" +#include "styles/style_layers.h" #include "styles/style_menu_icons.h" #include @@ -215,6 +219,14 @@ rpl::producer ListWidget::selectedListValue() const { collectSelectedItems()); } +void ListWidget::selectionAction(SelectionAction action) { + switch (action) { + case SelectionAction::Clear: clearSelected(); return; + case SelectionAction::Forward: forwardSelected(); return; + case SelectionAction::Delete: deleteSelected(); return; + } +} + QRect ListWidget::getCurrentSongGeometry() { const auto type = AudioMsgId::Type::Song; const auto current = ::Media::Player::instance()->current(type); @@ -307,11 +319,21 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems { } MessageIdsList ListWidget::collectSelectedIds() const { - const auto selected = collectSelectedItems(); + return collectSelectedIds(collectSelectedItems()); +} + +MessageIdsList ListWidget::collectSelectedIds( + const SelectedItems &items) const { + const auto session = &_controller->session(); return ranges::views::all( - selected.list - ) | ranges::views::transform([](const SelectedItem &item) { - return item.globalId.itemId; // #TODO downloads + items.list + ) | ranges::views::transform([](auto &&item) { + return item.globalId; + }) | ranges::views::filter([&](const GlobalMsgId &globalId) { + return (globalId.sessionUniqueId == session->uniqueId()) + && (session->data().message(globalId.itemId) != nullptr); + }) | ranges::views::transform([](const GlobalMsgId &globalId) { + return globalId.itemId; }) | ranges::to_vector; } @@ -806,19 +828,20 @@ void ListWidget::showContextMenu( auto link = ClickHandler::getActive(); - const auto itemFullId = item->fullId(); const auto owner = &session().data(); _contextMenu = base::make_unique_q( this, st::popupMenuWithIcons); - _contextMenu->addAction( - tr::lng_context_to_msg(tr::now), - [=] { - if (const auto item = owner->message(itemFullId)) { - _controller->parentController()->showPeerHistoryAtItem(item); - } - }, - &st::menuIconShowInChat); + if (item->isHistoryEntry()) { + _contextMenu->addAction( + tr::lng_context_to_msg(tr::now), + [=] { + if (const auto item = MessageByGlobalId(globalId)) { + goToMessageClickHandler(item)->onClick({}); + } + }, + &st::menuIconShowInChat); + } const auto lnkPhoto = link ? reinterpret_cast( @@ -870,12 +893,11 @@ void ListWidget::showContextMenu( this, [=] { DocumentSaveClickHandler::Save( - itemFullId, + globalId.itemId, lnkDocument, DocumentSaveClickHandler::Mode::ToNewFile); }); - if (item->history()->peer->allowsForwarding() - && !item->forbidsForward()) { + if (_provider->allowSaveFileAs(item, lnkDocument)) { _contextMenu->addAction( (isVideo ? tr::lng_context_save_video(tr::now) @@ -911,7 +933,9 @@ void ListWidget::showContextMenu( } if (canDeleteAll()) { _contextMenu->addAction( - tr::lng_context_delete_selected(tr::now), + (_controller->isDownloads() + ? u"Delete from disk"_q + : tr::lng_context_delete_selected(tr::now)), crl::guard(this, [this] { deleteSelected(); }), @@ -925,18 +949,28 @@ void ListWidget::showContextMenu( &st::menuIconSelect); } else { if (overSelected != SelectionState::NotOverSelectedItems) { - if (item->allowsForward()) { + const auto selectionData = _provider->computeSelectionData( + item, + FullSelection); + if (selectionData.canForward) { _contextMenu->addAction( tr::lng_context_forward_msg(tr::now), crl::guard(this, [=] { forwardItem(globalId); }), &st::menuIconForward); } - if (item->canDelete()) { - _contextMenu->addAction(Ui::DeleteMessageContextAction( - _contextMenu->menu(), - crl::guard(this, [=] { deleteItem(globalId); }), - item->ttlDestroyAt(), - [=] { _contextMenu = nullptr; })); + if (selectionData.canDelete) { + if (_controller->isDownloads()) { + _contextMenu->addAction( + u"Delete from disk"_q, + crl::guard(this, [=] { deleteItem(globalId); }), + &st::menuIconDelete); + } else { + _contextMenu->addAction(Ui::DeleteMessageContextAction( + _contextMenu->menu(), + crl::guard(this, [=] { deleteItem(globalId); }), + item->ttlDestroyAt(), + [=] { _contextMenu = nullptr; })); + } } } if (!_provider->hasSelectRestriction()) { @@ -1005,36 +1039,62 @@ void ListWidget::forwardItems(MessageIdsList &&items) { } void ListWidget::deleteSelected() { - if (const auto box = deleteItems(collectSelectedIds())) { - box->setDeleteConfirmedCallback(crl::guard(this, [=]{ - clearSelected(); - })); - } + deleteItems(collectSelectedItems(), crl::guard(this, [=]{ + clearSelected(); + })); } void ListWidget::deleteItem(GlobalMsgId globalId) { - const auto session = &_controller->session(); - if (globalId.sessionUniqueId == session->uniqueId()) { - if (const auto item = session->data().message(globalId.itemId)) { - deleteItems({ 1, item->fullId() }); + if (const auto item = MessageByGlobalId(globalId)) { + auto items = SelectedItems(_provider->type()); + items.list.push_back(SelectedItem(item->globalId())); + const auto selectionData = _provider->computeSelectionData( + item, + FullSelection); + items.list.back().canDelete = selectionData.canDelete; + items.list.back().canForward = selectionData.canForward; + deleteItems(std::move(items)); + } +} + +void ListWidget::deleteItems(SelectedItems &&items, Fn confirmed) { + const auto window = _controller->parentController(); + if (items.list.empty()) { + return; + } else if (_controller->isDownloads()) { + const auto phrase = (items.list.size() == 1) + ? u"Do you want to delete this file?"_q + : u"Do you want to delete X files?"_q; + const auto deleteSure = [=] { + const auto ids = ranges::views::all( + items.list + ) | ranges::views::transform([](const SelectedItem &item) { + return item.globalId; + }) | ranges::to_vector; + Core::App().downloadManager().deleteFiles(ids); + if (const auto box = _actionBoxWeak.data()) { + box->closeBox(); + } + }; + setActionBoxWeak(window->show(Box( + phrase, + tr::lng_box_delete(tr::now), + st::attentionBoxButton, + deleteSure))); + } else if (auto list = collectSelectedIds(items); !list.empty()) { + auto box = Box( + &_controller->session(), + std::move(list)); + const auto weak = box.data(); + window->show(std::move(box)); + setActionBoxWeak(weak); + if (confirmed) { + weak->setDeleteConfirmedCallback(std::move(confirmed)); } } - // #TODO downloads } -DeleteMessagesBox *ListWidget::deleteItems(MessageIdsList &&items) { - if (!items.empty()) { - const auto box = Ui::show( - Box( - &_controller->session(), - std::move(items))).data(); - setActionBoxWeak(box); - return box; - } - return nullptr; -} - -void ListWidget::setActionBoxWeak(QPointer box) { +void ListWidget::setActionBoxWeak(QPointer box) { if ((_actionBoxWeak = box)) { _actionBoxWeakLifetime = _actionBoxWeak->alive( ) | rpl::start_with_done([weak = Ui::MakeWeak(this)]{ @@ -1087,7 +1147,11 @@ void ListWidget::switchToWordSelection() { void ListWidget::applyItemSelection( HistoryItem *item, TextSelection selection) { - if (item && ChangeItemSelection(_selected, item, selection)) { + if (item + && ChangeItemSelection( + _selected, + item, + _provider->computeSelectionData(item, selection))) { repaintItem(item); pushSelectedItems(); } @@ -1624,7 +1688,10 @@ void ListWidget::applyDragSelection() { void ListWidget::applyDragSelection(SelectedMap &applyTo) const { if (_dragSelectAction == DragSelectAction::Selecting) { for (auto &[item, data] : _dragSelected) { - ChangeItemSelection(applyTo, item, FullSelection); + ChangeItemSelection( + applyTo, + item, + _provider->computeSelectionData(item, FullSelection)); } } else if (_dragSelectAction == DragSelectAction::Deselecting) { for (auto &[item, data] : _dragSelected) { diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.h b/Telegram/SourceFiles/info/media/info_media_list_widget.h index 77e0cbf684..bb35c27f43 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.h @@ -27,6 +27,7 @@ enum class PointState : char; namespace Ui { class PopupMenu; +class BoxContent; } // namespace Ui namespace Overview { @@ -65,9 +66,7 @@ public: rpl::producer scrollToRequests() const; rpl::producer selectedListValue() const; - void cancelSelection() { - clearSelected(); - } + void selectionAction(SelectionAction action); QRect getCurrentSongGeometry(); rpl::producer<> checkForHide() const { @@ -152,7 +151,6 @@ private: void setupSelectRestriction(); QMargins padding() const; - bool isMyItem(not_null item) const; bool isItemLayout( not_null item, BaseLayout *layout) const; @@ -168,6 +166,8 @@ private: [[nodiscard]] SelectedItems collectSelectedItems() const; [[nodiscard]] MessageIdsList collectSelectedIds() const; + [[nodiscard]] MessageIdsList collectSelectedIds( + const SelectedItems &items) const; void pushSelectedItems(); [[nodiscard]] bool hasSelected() const; [[nodiscard]] bool isSelectedItem( @@ -182,7 +182,7 @@ private: void forwardItems(MessageIdsList &&items); void deleteSelected(); void deleteItem(GlobalMsgId globalId); - DeleteMessagesBox *deleteItems(MessageIdsList &&items); + void deleteItems(SelectedItems &&items, Fn confirmed = nullptr); void applyItemSelection( HistoryItem *item, TextSelection selection); @@ -254,7 +254,7 @@ private: void checkMoveToOtherViewer(); void clearHeavyItems(); - void setActionBoxWeak(QPointer box); + void setActionBoxWeak(QPointer box); const not_null _controller; const std::unique_ptr _provider; @@ -290,7 +290,7 @@ private: base::unique_qptr _contextMenu; rpl::event_stream<> _checkForHide; - QPointer _actionBoxWeak; + QPointer _actionBoxWeak; rpl::lifetime _actionBoxWeakLifetime; QPoint _trippleClickPoint; diff --git a/Telegram/SourceFiles/info/media/info_media_provider.cpp b/Telegram/SourceFiles/info/media/info_media_provider.cpp index ba228a9632..63945d9652 100644 --- a/Telegram/SourceFiles/info/media/info_media_provider.cpp +++ b/Telegram/SourceFiles/info/media/info_media_provider.cpp @@ -461,6 +461,25 @@ std::unique_ptr Provider::createLayout( Unexpected("Type in ListWidget::createLayout()"); } +ListItemSelectionData Provider::computeSelectionData( + not_null item, + TextSelection selection) { + auto result = ListItemSelectionData(selection); + result.canDelete = item->canDelete(); + result.canForward = item->allowsForward(); + return result; +} + +bool Provider::allowSaveFileAs( + not_null item, + not_null document) { + return item->allowsForward(); +} + +std::optional Provider::deleteMenuPhrase() { + return std::nullopt; +} + void Provider::applyDragSelection( ListSelectedMap &selected, not_null fromItem, @@ -480,10 +499,11 @@ void Provider::applyDragSelection( for (auto &layoutItem : _layouts) { auto &&universalId = layoutItem.first; if (universalId <= fromId && universalId > tillId) { + const auto item = layoutItem.second.item->getItem(); ChangeItemSelection( selected, - layoutItem.second.item->getItem(), - FullSelection); + item, + computeSelectionData(item, FullSelection)); } } } diff --git a/Telegram/SourceFiles/info/media/info_media_provider.h b/Telegram/SourceFiles/info/media/info_media_provider.h index d6f9f2b8fc..8af8fd4317 100644 --- a/Telegram/SourceFiles/info/media/info_media_provider.h +++ b/Telegram/SourceFiles/info/media/info_media_provider.h @@ -46,6 +46,9 @@ public: not_null a, not_null b) override; + ListItemSelectionData computeSelectionData( + not_null item, + TextSelection selection) override; void applyDragSelection( ListSelectedMap &selected, not_null fromItem, @@ -53,6 +56,11 @@ public: not_null tillItem, bool skipTill) override; + bool allowSaveFileAs( + not_null item, + not_null document) override; + std::optional deleteMenuPhrase() override; + void saveState( not_null memento, ListScrollTopState scrollState) override; diff --git a/Telegram/SourceFiles/info/media/info_media_widget.cpp b/Telegram/SourceFiles/info/media/info_media_widget.cpp index 3161d18220..1e76ed8246 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_widget.cpp @@ -93,8 +93,8 @@ rpl::producer Widget::selectedListValue() const { return _inner->selectedListValue(); } -void Widget::cancelSelection() { - _inner->cancelSelection(); +void Widget::selectionAction(SelectionAction action) { + _inner->selectionAction(action); } void Widget::setIsStackBottom(bool isStackBottom) { diff --git a/Telegram/SourceFiles/info/media/info_media_widget.h b/Telegram/SourceFiles/info/media/info_media_widget.h index 3efcb1b7f6..6ac4d9e8ca 100644 --- a/Telegram/SourceFiles/info/media/info_media_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_widget.h @@ -99,7 +99,7 @@ public: not_null memento); rpl::producer selectedListValue() const override; - void cancelSelection() override; + void selectionAction(SelectionAction action) override; private: void saveState(not_null memento); diff --git a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp index a5a55e504a..7d0025bd1c 100644 --- a/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp +++ b/Telegram/SourceFiles/info/polls/info_polls_results_inner_widget.cpp @@ -679,7 +679,7 @@ void InnerWidget::setupContent() { st::boxRowPadding.right(), st::boxMediumSkip }); for (const auto &answer : _poll->answers) { - const auto session = &_controller->parentController()->session(); + const auto session = &_controller->session(); const auto controller = CreateAnswerRows( _content, _visibleTop.value(), diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 51319a9cfe..673e5d718b 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -924,7 +924,7 @@ Document::Document( const style::OverviewFileLayout &st) : RadialProgressItem(delegate, parent) , _data(fields.document) -, _msgl(goToMessageClickHandler(parent)) +, _msgl(parent->isHistoryEntry() ? goToMessageClickHandler(parent) : nullptr) , _namel(std::make_shared( _data, crl::guard(this, [=](FullMsgId id) { @@ -1189,7 +1189,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con p.drawTextLeft(nameleft, statustop, _width, _status.text()); } if (datetop >= 0 && clip.intersects(style::rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width))) { - p.setFont(ClickHandler::showAsActive(_msgl) ? st::normalFont->underline() : st::normalFont); + p.setFont((_msgl && ClickHandler::showAsActive(_msgl)) + ? st::normalFont->underline() + : st::normalFont); p.setPen(st::mediaInFg); p.drawTextLeft(nameleft, datetop, _width, _date, _datew); } diff --git a/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp b/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp index a3f1a6b565..a2f1ebcb37 100644 --- a/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp +++ b/Telegram/SourceFiles/ui/controls/delete_message_context_action.cpp @@ -106,7 +106,14 @@ void ActionWithTimer::paint(Painter &p) { paintRipple(p, 0, 0); } - st::menuIconDelete.paint(p, _st.itemIconPosition, width()); + const auto normalHeight = _st.itemPadding.top() + + _st.itemStyle.font->height + + _st.itemPadding.bottom(); + const auto deltaHeight = _height - normalHeight; + st::menuIconDelete.paint( + p, + _st.itemIconPosition + QPoint(0, deltaHeight / 2), + width()); p.setPen(selected ? _st.itemFgOver : _st.itemFg); _text.drawLeftElided( diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 54330cbb80..f853fbf6c3 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -1083,7 +1083,7 @@ void BlockSenderFromRepliesBox( Window::ClearReply{ id }); } -QPointer ShowForwardMessagesBox( +QPointer ShowForwardMessagesBox( not_null navigation, Data::ForwardDraft &&draft, FnMut &&successCallback) { @@ -1130,7 +1130,7 @@ QPointer ShowForwardMessagesBox( return weak->data(); } -QPointer ShowForwardMessagesBox( +QPointer ShowForwardMessagesBox( not_null navigation, MessageIdsList &&items, FnMut &&successCallback) { @@ -1140,7 +1140,7 @@ QPointer ShowForwardMessagesBox( std::move(successCallback)); } -QPointer ShowSendNowMessagesBox( +QPointer ShowSendNowMessagesBox( not_null navigation, not_null history, MessageIdsList &&items, diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h index 549a9c2e72..7989634335 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.h +++ b/Telegram/SourceFiles/window/window_peer_menu.h @@ -15,6 +15,7 @@ class History; namespace Ui { class RpWidget; +class BoxContent; class GenericBox; } // namespace Ui @@ -98,16 +99,16 @@ void ToggleHistoryArchived(not_null history, bool archived); Fn ClearHistoryHandler(not_null peer); Fn DeleteAndLeaveHandler(not_null peer); -QPointer ShowForwardMessagesBox( +QPointer ShowForwardMessagesBox( not_null navigation, Data::ForwardDraft &&draft, FnMut &&successCallback = nullptr); -QPointer ShowForwardMessagesBox( +QPointer ShowForwardMessagesBox( not_null navigation, MessageIdsList &&items, FnMut &&successCallback = nullptr); -QPointer ShowSendNowMessagesBox( +QPointer ShowSendNowMessagesBox( not_null navigation, not_null history, MessageIdsList &&items,