diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp b/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp index b379ef729..bcebd675d 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp +++ b/Telegram/SourceFiles/info/downloads/info_downloads_provider.cpp @@ -13,7 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_download_manager.h" #include "data/data_document.h" #include "data/data_media_types.h" +#include "data/data_session.h" +#include "main/main_session.h" +#include "main/main_account.h" #include "history/history_item.h" +#include "history/history.h" #include "core/application.h" #include "storage/storage_shared_media.h" #include "layout/layout_selection.h" @@ -47,6 +51,20 @@ rpl::producer Provider::hasSelectRestrictionChanges() { return rpl::never(); } +bool Provider::sectionHasFloatingHeader() { + return false; +} + +QString Provider::sectionTitle(not_null item) { + return QString(); +} + +bool Provider::sectionItemBelongsHere( + not_null item, + not_null previous) { + return true; +} + bool Provider::isPossiblyMyItem(not_null item) { return true; } @@ -85,6 +103,7 @@ void Provider::refreshViewer() { added = true; _downloading.emplace(item); _elements.push_back(Element{ item, id.started }); + trackItemSession(item); } } } @@ -104,6 +123,24 @@ void Provider::refreshViewer() { }, _lifetime); } +void Provider::trackItemSession(not_null item) { + const auto session = &item->history()->session(); + if (_trackedSessions.contains(session)) { + return; + } + auto &lifetime = _trackedSessions.emplace(session).first->second; + + session->data().itemRemoved( + ) | rpl::start_with_next([this](auto item) { + itemRemoved(item); + }, lifetime); + + session->account().sessionChanges( + ) | rpl::take(1) | rpl::start_with_next([=] { + _trackedSessions.remove(session); + }, lifetime); +} + rpl::producer<> Provider::refreshed() { return _refreshed.events(); } @@ -117,7 +154,9 @@ std::vector Provider::fillSections( return {}; } - auto result = std::vector(1, ListSection(Type::File)); + auto result = std::vector( + 1, + ListSection(Type::File, sectionDelegate())); auto §ion = result.back(); for (const auto &element : _elements) { if (auto layout = getLayout(element, delegate)) { @@ -172,6 +211,13 @@ bool Provider::isAfter( return false; } +void Provider::itemRemoved(not_null item) { + if (const auto i = _layouts.find(item); i != end(_layouts)) { + _layoutRemoved.fire(i->second.item.get()); + _layouts.erase(i); + } +} + BaseLayout *Provider::getLayout( Element element, not_null delegate) { diff --git a/Telegram/SourceFiles/info/downloads/info_downloads_provider.h b/Telegram/SourceFiles/info/downloads/info_downloads_provider.h index f402a9f4a..777db1be9 100644 --- a/Telegram/SourceFiles/info/downloads/info_downloads_provider.h +++ b/Telegram/SourceFiles/info/downloads/info_downloads_provider.h @@ -15,7 +15,9 @@ class AbstractController; namespace Info::Downloads { -class Provider final : public Media::ListProvider { +class Provider final + : public Media::ListProvider + , private Media::ListSectionDelegate { public: explicit Provider(not_null controller); @@ -65,10 +67,18 @@ private: int64 started = 0; // unixtime * 1000 }; + bool sectionHasFloatingHeader() override; + QString sectionTitle(not_null item) override; + bool sectionItemBelongsHere( + not_null item, + not_null previous) override; + void itemRemoved(not_null item); void markLayoutsStale(); void clearStaleLayouts(); + void trackItemSession(not_null item); + [[nodiscard]] Media::BaseLayout *getLayout( Element element, not_null delegate); @@ -83,10 +93,14 @@ private: base::flat_set> _downloading; base::flat_set> _downloaded; - std::unordered_map, Media::CachedItem> _layouts; + std::unordered_map< + not_null, + Media::CachedItem> _layouts; rpl::event_stream> _layoutRemoved; rpl::event_stream<> _refreshed; + base::flat_map, rpl::lifetime> _trackedSessions; + rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/info/media/info_media_common.h b/Telegram/SourceFiles/info/media/info_media_common.h index a1edf3306..d55b555b9 100644 --- a/Telegram/SourceFiles/info/media/info_media_common.h +++ b/Telegram/SourceFiles/info/media/info_media_common.h @@ -85,6 +85,20 @@ bool ChangeItemSelection( not_null item, TextSelection selection); +class ListSectionDelegate { +public: + [[nodiscard]] virtual bool sectionHasFloatingHeader() = 0; + [[nodiscard]] virtual QString sectionTitle( + not_null item) = 0; + [[nodiscard]] virtual bool sectionItemBelongsHere( + not_null item, + not_null previous) = 0; + + [[nodiscard]] not_null sectionDelegate() { + return this; + } +}; + class ListProvider { public: [[nodiscard]] virtual Type type() = 0; @@ -133,7 +147,6 @@ public: Fn restoreScrollState) = 0; virtual ~ListProvider() = default; - }; } // namespace Info::Media diff --git a/Telegram/SourceFiles/info/media/info_media_list_section.cpp b/Telegram/SourceFiles/info/media/info_media_list_section.cpp index 280e48561..4f1d9e964 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_section.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_section.cpp @@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/media/info_media_list_section.h" #include "storage/storage_shared_media.h" -#include "lang/lang_keys.h" #include "layout/layout_selection.h" #include "styles/style_info.h" @@ -17,27 +16,12 @@ namespace { constexpr auto kFloatingHeaderAlpha = 0.9; -[[nodiscard]] bool HasFloatingHeader(Type type) { - switch (type) { - case Type::Photo: - case Type::GIF: - case Type::Video: - case Type::RoundFile: - case Type::RoundVoiceFile: - case Type::MusicFile: - return false; - case Type::File: - case Type::Link: - return true; - } - Unexpected("Type in HasFloatingHeader()"); -} - } // namespace -ListSection::ListSection(Type type) +ListSection::ListSection(Type type, not_null delegate) : _type(type) -, _hasFloatingHeader(HasFloatingHeader(type)) +, _delegate(delegate) +, _hasFloatingHeader(delegate->sectionHasFloatingHeader()) , _mosaic(st::emojiPanWidth - st::inlineResultsLeft) { } @@ -87,54 +71,14 @@ void ListSection::finishSection() { } void ListSection::setHeader(not_null item) { - auto text = [&] { - auto date = item->dateTime().date(); - switch (_type) { - case Type::Photo: - case Type::GIF: - case Type::Video: - case Type::RoundFile: - case Type::RoundVoiceFile: - case Type::File: - return langMonthFull(date); - - case Type::Link: - return langDayOfMonthFull(date); - - case Type::MusicFile: - return QString(); - } - Unexpected("Type in ListSection::setHeader()"); - }(); - _header.setText(st::infoMediaHeaderStyle, text); + _header.setText(st::infoMediaHeaderStyle, _delegate->sectionTitle(item)); } bool ListSection::belongsHere( not_null item) const { Expects(!_items.empty()); - auto date = item->dateTime().date(); - auto myDate = _items.back()->dateTime().date(); - - switch (_type) { - case Type::Photo: - case Type::GIF: - case Type::Video: - case Type::RoundFile: - case Type::RoundVoiceFile: - case Type::File: - return date.year() == myDate.year() - && date.month() == myDate.month(); - - case Type::Link: - return date.year() == myDate.year() - && date.month() == myDate.month() - && date.day() == myDate.day(); - - case Type::MusicFile: - return true; - } - Unexpected("Type in ListSection::belongsHere()"); + return _delegate->sectionItemBelongsHere(item, _items.back()); } void ListSection::appendItem(not_null item) { @@ -279,6 +223,10 @@ auto ListSection::findItemAfterBottom( }); } +const ListSection::Items &ListSection::items() const { + return _items; +} + void ListSection::paint( Painter &p, const ListContext &context, diff --git a/Telegram/SourceFiles/info/media/info_media_list_section.h b/Telegram/SourceFiles/info/media/info_media_list_section.h index 7a394d2e9..e03660bf0 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_section.h +++ b/Telegram/SourceFiles/info/media/info_media_list_section.h @@ -15,7 +15,7 @@ namespace Info::Media { class ListSection { public: - ListSection(Type type); + ListSection(Type type, not_null delegate); bool addItem(not_null item); void finishSection(); @@ -40,6 +40,9 @@ public: not_null item) const; [[nodiscard]] ListFoundItem findItemByPoint(QPoint point) const; + using Items = std::vector>; + const Items &items() const; + void paint( Painter &p, const ListContext &context, @@ -49,8 +52,6 @@ public: void paintFloatingHeader(Painter &p, int visibleTop, int outerWidth); private: - using Items = std::vector>; - [[nodiscard]] int headerHeight() const; void appendItem(not_null item); void setHeader(not_null item); @@ -72,6 +73,8 @@ private: void refreshHeight(); Type _type = Type{}; + not_null _delegate; + bool _hasFloatingHeader = false; Ui::Text::String _header; Items _items; diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 0ee0c2007..462f5e8d0 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/inactive_press.h" #include "lang/lang_keys.h" #include "main/main_session.h" +#include "main/main_account.h" #include "mainwidget.h" #include "mainwindow.h" #include "base/platform/base_platform_info.h" @@ -158,7 +159,7 @@ void ListWidget::start() { if (_controller->isDownloads()) { _provider->refreshViewer(); } else { - subscribeToSession(&session()); + subscribeToSession(&session(), lifetime()); _controller->mediaSourceQueryValue( ) | rpl::start_with_next([this] { @@ -169,26 +170,28 @@ void ListWidget::start() { setupSelectRestriction(); } -void ListWidget::subscribeToSession(not_null session) { +void ListWidget::subscribeToSession( + not_null session, + rpl::lifetime &lifetime) { session->downloaderTaskFinished( ) | rpl::start_with_next([=] { update(); - }, lifetime()); + }, lifetime); session->data().itemLayoutChanged( ) | rpl::start_with_next([this](auto item) { itemLayoutChanged(item); - }, lifetime()); + }, lifetime); session->data().itemRemoved( ) | rpl::start_with_next([this](auto item) { itemRemoved(item); - }, lifetime()); + }, lifetime); session->data().itemRepaintRequest( ) | rpl::start_with_next([this](auto item) { repaintItem(item); - }, lifetime()); + }, lifetime); } void ListWidget::setupSelectRestriction() { @@ -416,12 +419,30 @@ void ListWidget::openDocument( showInMediaView); } +void ListWidget::trackSession(not_null session) { + if (_trackedSessions.contains(session)) { + return; + } + auto &lifetime = _trackedSessions.emplace(session).first->second; + subscribeToSession(session, lifetime); + session->account().sessionChanges( + ) | rpl::take(1) | rpl::start_with_next([=] { + _trackedSessions.remove(session); + }, lifetime); +} + void ListWidget::refreshRows() { saveScrollState(); _sections.clear(); _sections = _provider->fillSections(this); + if (_controller->isDownloads() && !_sections.empty()) { + for (const auto &item : _sections.back().items()) { + trackSession(&item->getItem()->history()->session()); + } + } + if (const auto count = _provider->fullCount()) { if (*count > kMediaCountForSearch) { _controller->setSearchEnabledByContent(true); diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.h b/Telegram/SourceFiles/info/media/info_media_list_widget.h index 3aed448c3..77e0cbf68 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.h @@ -145,7 +145,9 @@ private: void start(); int recountHeight(); void refreshHeight(); - void subscribeToSession(not_null session); + void subscribeToSession( + not_null session, + rpl::lifetime &lifetime); void setupSelectRestriction(); @@ -162,6 +164,7 @@ private: void refreshViewer(); void refreshRows(); + void trackSession(not_null session); [[nodiscard]] SelectedItems collectSelectedItems() const; [[nodiscard]] MessageIdsList collectSelectedIds() const; @@ -283,6 +286,7 @@ private: bool _wasSelectedText = false; // was some text selected in current drag action const std::unique_ptr _dateBadge; + base::flat_map, rpl::lifetime> _trackedSessions; base::unique_qptr _contextMenu; rpl::event_stream<> _checkForHide; diff --git a/Telegram/SourceFiles/info/media/info_media_provider.cpp b/Telegram/SourceFiles/info/media/info_media_provider.cpp index f4ff2bfc3..72fecbc4e 100644 --- a/Telegram/SourceFiles/info/media/info_media_provider.cpp +++ b/Telegram/SourceFiles/info/media/info_media_provider.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_controller.h" #include "layout/layout_selection.h" #include "main/main_session.h" +#include "lang/lang_keys.h" #include "history/history.h" #include "history/history_item.h" #include "data/data_session.h" @@ -121,6 +122,66 @@ rpl::producer Provider::hasSelectRestrictionChanges() { }) | rpl::distinct_until_changed() | rpl::skip(1); } +bool Provider::sectionHasFloatingHeader() { + switch (_type) { + case Type::Photo: + case Type::GIF: + case Type::Video: + case Type::RoundFile: + case Type::RoundVoiceFile: + case Type::MusicFile: + return false; + case Type::File: + case Type::Link: + return true; + } + Unexpected("Type in HasFloatingHeader()"); +} + +QString Provider::sectionTitle(not_null item) { + switch (_type) { + case Type::Photo: + case Type::GIF: + case Type::Video: + case Type::RoundFile: + case Type::RoundVoiceFile: + case Type::File: + return langMonthFull(item->dateTime().date()); + + case Type::Link: + return langDayOfMonthFull(item->dateTime().date()); + + case Type::MusicFile: + return QString(); + } + Unexpected("Type in ListSection::setHeader()"); +} + +bool Provider::sectionItemBelongsHere( + not_null item, + not_null previous) { + const auto date = item->dateTime().date(); + const auto sectionDate = previous->dateTime().date(); + + switch (_type) { + case Type::Photo: + case Type::GIF: + case Type::Video: + case Type::RoundFile: + case Type::RoundVoiceFile: + case Type::File: + return date.year() == sectionDate.year() + && date.month() == sectionDate.month(); + + case Type::Link: + return date == sectionDate; + + case Type::MusicFile: + return true; + } + Unexpected("Type in ListSection::belongsHere()"); +} + bool Provider::isPossiblyMyItem(not_null item) { return isPossiblyMyPeerId(item->history()->peer->id); } @@ -221,7 +282,7 @@ std::vector Provider::fillSections( const auto guard = gsl::finally([&] { clearStaleLayouts(); }); auto result = std::vector(); - auto section = ListSection(_type); + auto section = ListSection(_type, sectionDelegate()); auto count = _slice.size(); for (auto i = count; i != 0;) { auto universalId = GetUniversalId(_slice[--i]); @@ -229,7 +290,7 @@ std::vector Provider::fillSections( if (!section.addItem(layout)) { section.finishSection(); result.push_back(std::move(section)); - section = ListSection(_type); + section = ListSection(_type, sectionDelegate()); section.addItem(layout); } } diff --git a/Telegram/SourceFiles/info/media/info_media_provider.h b/Telegram/SourceFiles/info/media/info_media_provider.h index c9349b1d6..d6f9f2b8f 100644 --- a/Telegram/SourceFiles/info/media/info_media_provider.h +++ b/Telegram/SourceFiles/info/media/info_media_provider.h @@ -16,7 +16,7 @@ class AbstractController; namespace Info::Media { -class Provider final : public ListProvider { +class Provider final : public ListProvider, private ListSectionDelegate { public: explicit Provider(not_null controller); @@ -64,6 +64,12 @@ private: static constexpr auto kMinimalIdsLimit = 16; static constexpr auto kDefaultAroundId = (ServerMaxMsgId - 1); + bool sectionHasFloatingHeader() override; + QString sectionTitle(not_null item) override; + bool sectionItemBelongsHere( + not_null item, + not_null previous) override; + [[nodiscard]] bool isPossiblyMyPeerId(PeerId peerId) const; [[nodiscard]] FullMsgId computeFullId(UniversalMsgId universalId) const; [[nodiscard]] BaseLayout *getLayout(