Track all required sessions notifications.

This commit is contained in:
John Preston 2022-02-24 21:19:27 +03:00
parent 93c6038992
commit e6294f48de
9 changed files with 194 additions and 78 deletions

View file

@ -13,7 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_download_manager.h" #include "data/data_download_manager.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_media_types.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_item.h"
#include "history/history.h"
#include "core/application.h" #include "core/application.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "layout/layout_selection.h" #include "layout/layout_selection.h"
@ -47,6 +51,20 @@ rpl::producer<bool> Provider::hasSelectRestrictionChanges() {
return rpl::never<bool>(); return rpl::never<bool>();
} }
bool Provider::sectionHasFloatingHeader() {
return false;
}
QString Provider::sectionTitle(not_null<const BaseLayout*> item) {
return QString();
}
bool Provider::sectionItemBelongsHere(
not_null<const BaseLayout*> item,
not_null<const BaseLayout*> previous) {
return true;
}
bool Provider::isPossiblyMyItem(not_null<const HistoryItem*> item) { bool Provider::isPossiblyMyItem(not_null<const HistoryItem*> item) {
return true; return true;
} }
@ -85,6 +103,7 @@ void Provider::refreshViewer() {
added = true; added = true;
_downloading.emplace(item); _downloading.emplace(item);
_elements.push_back(Element{ item, id.started }); _elements.push_back(Element{ item, id.started });
trackItemSession(item);
} }
} }
} }
@ -104,6 +123,24 @@ void Provider::refreshViewer() {
}, _lifetime); }, _lifetime);
} }
void Provider::trackItemSession(not_null<const HistoryItem*> 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() { rpl::producer<> Provider::refreshed() {
return _refreshed.events(); return _refreshed.events();
} }
@ -117,7 +154,9 @@ std::vector<ListSection> Provider::fillSections(
return {}; return {};
} }
auto result = std::vector<ListSection>(1, ListSection(Type::File)); auto result = std::vector<ListSection>(
1,
ListSection(Type::File, sectionDelegate()));
auto &section = result.back(); auto &section = result.back();
for (const auto &element : _elements) { for (const auto &element : _elements) {
if (auto layout = getLayout(element, delegate)) { if (auto layout = getLayout(element, delegate)) {
@ -172,6 +211,13 @@ bool Provider::isAfter(
return false; return false;
} }
void Provider::itemRemoved(not_null<const HistoryItem*> item) {
if (const auto i = _layouts.find(item); i != end(_layouts)) {
_layoutRemoved.fire(i->second.item.get());
_layouts.erase(i);
}
}
BaseLayout *Provider::getLayout( BaseLayout *Provider::getLayout(
Element element, Element element,
not_null<Overview::Layout::Delegate*> delegate) { not_null<Overview::Layout::Delegate*> delegate) {

View file

@ -15,7 +15,9 @@ class AbstractController;
namespace Info::Downloads { namespace Info::Downloads {
class Provider final : public Media::ListProvider { class Provider final
: public Media::ListProvider
, private Media::ListSectionDelegate {
public: public:
explicit Provider(not_null<AbstractController*> controller); explicit Provider(not_null<AbstractController*> controller);
@ -65,10 +67,18 @@ private:
int64 started = 0; // unixtime * 1000 int64 started = 0; // unixtime * 1000
}; };
bool sectionHasFloatingHeader() override;
QString sectionTitle(not_null<const Media::BaseLayout*> item) override;
bool sectionItemBelongsHere(
not_null<const Media::BaseLayout*> item,
not_null<const Media::BaseLayout*> previous) override;
void itemRemoved(not_null<const HistoryItem*> item); void itemRemoved(not_null<const HistoryItem*> item);
void markLayoutsStale(); void markLayoutsStale();
void clearStaleLayouts(); void clearStaleLayouts();
void trackItemSession(not_null<const HistoryItem*> item);
[[nodiscard]] Media::BaseLayout *getLayout( [[nodiscard]] Media::BaseLayout *getLayout(
Element element, Element element,
not_null<Overview::Layout::Delegate*> delegate); not_null<Overview::Layout::Delegate*> delegate);
@ -83,10 +93,14 @@ private:
base::flat_set<not_null<const HistoryItem*>> _downloading; base::flat_set<not_null<const HistoryItem*>> _downloading;
base::flat_set<not_null<const HistoryItem*>> _downloaded; base::flat_set<not_null<const HistoryItem*>> _downloaded;
std::unordered_map<not_null<HistoryItem*>, Media::CachedItem> _layouts; std::unordered_map<
not_null<const HistoryItem*>,
Media::CachedItem> _layouts;
rpl::event_stream<not_null<Media::BaseLayout*>> _layoutRemoved; rpl::event_stream<not_null<Media::BaseLayout*>> _layoutRemoved;
rpl::event_stream<> _refreshed; rpl::event_stream<> _refreshed;
base::flat_map<not_null<Main::Session*>, rpl::lifetime> _trackedSessions;
rpl::lifetime _lifetime; rpl::lifetime _lifetime;
}; };

View file

@ -85,6 +85,20 @@ bool ChangeItemSelection(
not_null<const HistoryItem*> item, not_null<const HistoryItem*> item,
TextSelection selection); TextSelection selection);
class ListSectionDelegate {
public:
[[nodiscard]] virtual bool sectionHasFloatingHeader() = 0;
[[nodiscard]] virtual QString sectionTitle(
not_null<const BaseLayout*> item) = 0;
[[nodiscard]] virtual bool sectionItemBelongsHere(
not_null<const BaseLayout*> item,
not_null<const BaseLayout*> previous) = 0;
[[nodiscard]] not_null<ListSectionDelegate*> sectionDelegate() {
return this;
}
};
class ListProvider { class ListProvider {
public: public:
[[nodiscard]] virtual Type type() = 0; [[nodiscard]] virtual Type type() = 0;
@ -133,7 +147,6 @@ public:
Fn<void(ListScrollTopState)> restoreScrollState) = 0; Fn<void(ListScrollTopState)> restoreScrollState) = 0;
virtual ~ListProvider() = default; virtual ~ListProvider() = default;
}; };
} // namespace Info::Media } // namespace Info::Media

View file

@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/media/info_media_list_section.h" #include "info/media/info_media_list_section.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "lang/lang_keys.h"
#include "layout/layout_selection.h" #include "layout/layout_selection.h"
#include "styles/style_info.h" #include "styles/style_info.h"
@ -17,27 +16,12 @@ namespace {
constexpr auto kFloatingHeaderAlpha = 0.9; 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 } // namespace
ListSection::ListSection(Type type) ListSection::ListSection(Type type, not_null<ListSectionDelegate*> delegate)
: _type(type) : _type(type)
, _hasFloatingHeader(HasFloatingHeader(type)) , _delegate(delegate)
, _hasFloatingHeader(delegate->sectionHasFloatingHeader())
, _mosaic(st::emojiPanWidth - st::inlineResultsLeft) { , _mosaic(st::emojiPanWidth - st::inlineResultsLeft) {
} }
@ -87,54 +71,14 @@ void ListSection::finishSection() {
} }
void ListSection::setHeader(not_null<BaseLayout*> item) { void ListSection::setHeader(not_null<BaseLayout*> item) {
auto text = [&] { _header.setText(st::infoMediaHeaderStyle, _delegate->sectionTitle(item));
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);
} }
bool ListSection::belongsHere( bool ListSection::belongsHere(
not_null<BaseLayout*> item) const { not_null<BaseLayout*> item) const {
Expects(!_items.empty()); Expects(!_items.empty());
auto date = item->dateTime().date(); return _delegate->sectionItemBelongsHere(item, _items.back());
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()");
} }
void ListSection::appendItem(not_null<BaseLayout*> item) { void ListSection::appendItem(not_null<BaseLayout*> item) {
@ -279,6 +223,10 @@ auto ListSection::findItemAfterBottom(
}); });
} }
const ListSection::Items &ListSection::items() const {
return _items;
}
void ListSection::paint( void ListSection::paint(
Painter &p, Painter &p,
const ListContext &context, const ListContext &context,

View file

@ -15,7 +15,7 @@ namespace Info::Media {
class ListSection { class ListSection {
public: public:
ListSection(Type type); ListSection(Type type, not_null<ListSectionDelegate*> delegate);
bool addItem(not_null<BaseLayout*> item); bool addItem(not_null<BaseLayout*> item);
void finishSection(); void finishSection();
@ -40,6 +40,9 @@ public:
not_null<BaseLayout*> item) const; not_null<BaseLayout*> item) const;
[[nodiscard]] ListFoundItem findItemByPoint(QPoint point) const; [[nodiscard]] ListFoundItem findItemByPoint(QPoint point) const;
using Items = std::vector<not_null<BaseLayout*>>;
const Items &items() const;
void paint( void paint(
Painter &p, Painter &p,
const ListContext &context, const ListContext &context,
@ -49,8 +52,6 @@ public:
void paintFloatingHeader(Painter &p, int visibleTop, int outerWidth); void paintFloatingHeader(Painter &p, int visibleTop, int outerWidth);
private: private:
using Items = std::vector<not_null<BaseLayout*>>;
[[nodiscard]] int headerHeight() const; [[nodiscard]] int headerHeight() const;
void appendItem(not_null<BaseLayout*> item); void appendItem(not_null<BaseLayout*> item);
void setHeader(not_null<BaseLayout*> item); void setHeader(not_null<BaseLayout*> item);
@ -72,6 +73,8 @@ private:
void refreshHeight(); void refreshHeight();
Type _type = Type{}; Type _type = Type{};
not_null<ListSectionDelegate*> _delegate;
bool _hasFloatingHeader = false; bool _hasFloatingHeader = false;
Ui::Text::String _header; Ui::Text::String _header;
Items _items; Items _items;

View file

@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/inactive_press.h" #include "ui/inactive_press.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_account.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
@ -158,7 +159,7 @@ void ListWidget::start() {
if (_controller->isDownloads()) { if (_controller->isDownloads()) {
_provider->refreshViewer(); _provider->refreshViewer();
} else { } else {
subscribeToSession(&session()); subscribeToSession(&session(), lifetime());
_controller->mediaSourceQueryValue( _controller->mediaSourceQueryValue(
) | rpl::start_with_next([this] { ) | rpl::start_with_next([this] {
@ -169,26 +170,28 @@ void ListWidget::start() {
setupSelectRestriction(); setupSelectRestriction();
} }
void ListWidget::subscribeToSession(not_null<Main::Session*> session) { void ListWidget::subscribeToSession(
not_null<Main::Session*> session,
rpl::lifetime &lifetime) {
session->downloaderTaskFinished( session->downloaderTaskFinished(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
update(); update();
}, lifetime()); }, lifetime);
session->data().itemLayoutChanged( session->data().itemLayoutChanged(
) | rpl::start_with_next([this](auto item) { ) | rpl::start_with_next([this](auto item) {
itemLayoutChanged(item); itemLayoutChanged(item);
}, lifetime()); }, lifetime);
session->data().itemRemoved( session->data().itemRemoved(
) | rpl::start_with_next([this](auto item) { ) | rpl::start_with_next([this](auto item) {
itemRemoved(item); itemRemoved(item);
}, lifetime()); }, lifetime);
session->data().itemRepaintRequest( session->data().itemRepaintRequest(
) | rpl::start_with_next([this](auto item) { ) | rpl::start_with_next([this](auto item) {
repaintItem(item); repaintItem(item);
}, lifetime()); }, lifetime);
} }
void ListWidget::setupSelectRestriction() { void ListWidget::setupSelectRestriction() {
@ -416,12 +419,30 @@ void ListWidget::openDocument(
showInMediaView); showInMediaView);
} }
void ListWidget::trackSession(not_null<Main::Session*> 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() { void ListWidget::refreshRows() {
saveScrollState(); saveScrollState();
_sections.clear(); _sections.clear();
_sections = _provider->fillSections(this); _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 (const auto count = _provider->fullCount()) {
if (*count > kMediaCountForSearch) { if (*count > kMediaCountForSearch) {
_controller->setSearchEnabledByContent(true); _controller->setSearchEnabledByContent(true);

View file

@ -145,7 +145,9 @@ private:
void start(); void start();
int recountHeight(); int recountHeight();
void refreshHeight(); void refreshHeight();
void subscribeToSession(not_null<Main::Session*> session); void subscribeToSession(
not_null<Main::Session*> session,
rpl::lifetime &lifetime);
void setupSelectRestriction(); void setupSelectRestriction();
@ -162,6 +164,7 @@ private:
void refreshViewer(); void refreshViewer();
void refreshRows(); void refreshRows();
void trackSession(not_null<Main::Session*> session);
[[nodiscard]] SelectedItems collectSelectedItems() const; [[nodiscard]] SelectedItems collectSelectedItems() const;
[[nodiscard]] MessageIdsList collectSelectedIds() const; [[nodiscard]] MessageIdsList collectSelectedIds() const;
@ -283,6 +286,7 @@ private:
bool _wasSelectedText = false; // was some text selected in current drag action bool _wasSelectedText = false; // was some text selected in current drag action
const std::unique_ptr<DateBadge> _dateBadge; const std::unique_ptr<DateBadge> _dateBadge;
base::flat_map<not_null<Main::Session*>, rpl::lifetime> _trackedSessions;
base::unique_qptr<Ui::PopupMenu> _contextMenu; base::unique_qptr<Ui::PopupMenu> _contextMenu;
rpl::event_stream<> _checkForHide; rpl::event_stream<> _checkForHide;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_controller.h" #include "info/info_controller.h"
#include "layout/layout_selection.h" #include "layout/layout_selection.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "lang/lang_keys.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -121,6 +122,66 @@ rpl::producer<bool> Provider::hasSelectRestrictionChanges() {
}) | rpl::distinct_until_changed() | rpl::skip(1); }) | 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<const BaseLayout*> 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<const BaseLayout*> item,
not_null<const BaseLayout*> 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<const HistoryItem*> item) { bool Provider::isPossiblyMyItem(not_null<const HistoryItem*> item) {
return isPossiblyMyPeerId(item->history()->peer->id); return isPossiblyMyPeerId(item->history()->peer->id);
} }
@ -221,7 +282,7 @@ std::vector<ListSection> Provider::fillSections(
const auto guard = gsl::finally([&] { clearStaleLayouts(); }); const auto guard = gsl::finally([&] { clearStaleLayouts(); });
auto result = std::vector<ListSection>(); auto result = std::vector<ListSection>();
auto section = ListSection(_type); auto section = ListSection(_type, sectionDelegate());
auto count = _slice.size(); auto count = _slice.size();
for (auto i = count; i != 0;) { for (auto i = count; i != 0;) {
auto universalId = GetUniversalId(_slice[--i]); auto universalId = GetUniversalId(_slice[--i]);
@ -229,7 +290,7 @@ std::vector<ListSection> Provider::fillSections(
if (!section.addItem(layout)) { if (!section.addItem(layout)) {
section.finishSection(); section.finishSection();
result.push_back(std::move(section)); result.push_back(std::move(section));
section = ListSection(_type); section = ListSection(_type, sectionDelegate());
section.addItem(layout); section.addItem(layout);
} }
} }

View file

@ -16,7 +16,7 @@ class AbstractController;
namespace Info::Media { namespace Info::Media {
class Provider final : public ListProvider { class Provider final : public ListProvider, private ListSectionDelegate {
public: public:
explicit Provider(not_null<AbstractController*> controller); explicit Provider(not_null<AbstractController*> controller);
@ -64,6 +64,12 @@ private:
static constexpr auto kMinimalIdsLimit = 16; static constexpr auto kMinimalIdsLimit = 16;
static constexpr auto kDefaultAroundId = (ServerMaxMsgId - 1); static constexpr auto kDefaultAroundId = (ServerMaxMsgId - 1);
bool sectionHasFloatingHeader() override;
QString sectionTitle(not_null<const BaseLayout*> item) override;
bool sectionItemBelongsHere(
not_null<const BaseLayout*> item,
not_null<const BaseLayout*> previous) override;
[[nodiscard]] bool isPossiblyMyPeerId(PeerId peerId) const; [[nodiscard]] bool isPossiblyMyPeerId(PeerId peerId) const;
[[nodiscard]] FullMsgId computeFullId(UniversalMsgId universalId) const; [[nodiscard]] FullMsgId computeFullId(UniversalMsgId universalId) const;
[[nodiscard]] BaseLayout *getLayout( [[nodiscard]] BaseLayout *getLayout(