From b8bdca8921851b6ac48de8088e95284e1dc20793 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 13 Oct 2022 14:32:03 +0400 Subject: [PATCH] Extract Data::Thread common for History / ForumTopic. --- .../SourceFiles/api/api_unread_things.cpp | 88 ++++---- Telegram/SourceFiles/api/api_unread_things.h | 28 ++- Telegram/SourceFiles/data/data_forum.cpp | 10 +- .../SourceFiles/data/data_forum_topic.cpp | 38 +--- Telegram/SourceFiles/data/data_forum_topic.h | 28 +-- Telegram/SourceFiles/data/data_peer.cpp | 10 +- Telegram/SourceFiles/data/data_session.cpp | 7 +- Telegram/SourceFiles/data/data_thread.cpp | 133 ++++++++++++ Telegram/SourceFiles/data/data_thread.h | 112 ++++++++++ .../data/notify/data_notify_settings.cpp | 18 +- .../SourceFiles/dialogs/dialogs_entry.cpp | 78 +------ Telegram/SourceFiles/dialogs/dialogs_entry.h | 33 +-- Telegram/SourceFiles/dialogs/dialogs_key.cpp | 14 +- Telegram/SourceFiles/dialogs/dialogs_key.h | 2 + Telegram/SourceFiles/dialogs/dialogs_row.h | 3 + .../SourceFiles/dialogs/ui/dialogs_layout.cpp | 34 ++- Telegram/SourceFiles/history/history.cpp | 119 ++++------- Telegram/SourceFiles/history/history.h | 58 ++--- .../history/history_inner_widget.cpp | 1 + Telegram/SourceFiles/history/history_item.cpp | 25 ++- Telegram/SourceFiles/history/history_item.h | 2 + .../history/history_unread_things.cpp | 25 +-- .../history/history_unread_things.h | 11 +- .../SourceFiles/history/history_widget.cpp | 3 +- Telegram/SourceFiles/history/history_widget.h | 2 +- .../view/history_view_corner_buttons.cpp | 56 +++-- .../view/history_view_corner_buttons.h | 7 +- .../view/history_view_pinned_section.cpp | 2 +- .../view/history_view_pinned_section.h | 2 +- .../view/history_view_replies_section.cpp | 4 +- .../view/history_view_replies_section.h | 2 +- .../view/history_view_scheduled_section.cpp | 2 +- .../view/history_view_scheduled_section.h | 2 +- Telegram/SourceFiles/menu/menu_send.cpp | 60 +++--- Telegram/SourceFiles/menu/menu_send.h | 10 +- .../window/notifications_manager.cpp | 198 +++++++++--------- .../window/notifications_manager.h | 63 +++--- .../window/notifications_manager_default.cpp | 12 +- 38 files changed, 675 insertions(+), 627 deletions(-) create mode 100644 Telegram/SourceFiles/data/data_thread.cpp create mode 100644 Telegram/SourceFiles/data/data_thread.h diff --git a/Telegram/SourceFiles/api/api_unread_things.cpp b/Telegram/SourceFiles/api/api_unread_things.cpp index 5e8b624af..f0729fe35 100644 --- a/Telegram/SourceFiles/api/api_unread_things.cpp +++ b/Telegram/SourceFiles/api/api_unread_things.cpp @@ -24,39 +24,27 @@ constexpr auto kPreloadIfLess = 5; constexpr auto kFirstRequestLimit = 10; constexpr auto kNextRequestLimit = 100; - -[[nodiscard]] not_null ResolveHistory( - not_null entry) { - if (const auto history = entry->asHistory()) { - return history; - } - const auto topic = entry->asTopic(); - - Ensures(topic != nullptr); - return topic->history(); -} - } // namespace UnreadThings::UnreadThings(not_null api) : _api(api) { } -bool UnreadThings::trackMentions(DialogsEntry *entry) const { - const auto peer = entry ? ResolveHistory(entry)->peer.get() : nullptr; +bool UnreadThings::trackMentions(Data::Thread *thread) const { + const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr; return peer && (peer->isChat() || peer->isMegagroup()); } -bool UnreadThings::trackReactions(DialogsEntry *entry) const { - const auto peer = entry ? ResolveHistory(entry)->peer.get() : nullptr; +bool UnreadThings::trackReactions(Data::Thread *thread) const { + const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr; return peer && (peer->isChat() || peer->isMegagroup()); } -void UnreadThings::preloadEnough(DialogsEntry *entry) { - if (trackMentions(entry)) { - preloadEnoughMentions(entry); +void UnreadThings::preloadEnough(Data::Thread *thread) { + if (trackMentions(thread)) { + preloadEnoughMentions(thread); } - if (trackReactions(entry)) { - preloadEnoughReactions(entry); + if (trackReactions(thread)) { + preloadEnoughReactions(thread); } } @@ -75,48 +63,48 @@ void UnreadThings::mediaAndMentionsRead( } } -void UnreadThings::preloadEnoughMentions(not_null entry) { - const auto fullCount = entry->unreadMentions().count(); - const auto loadedCount = entry->unreadMentions().loadedCount(); +void UnreadThings::preloadEnoughMentions(not_null thread) { + const auto fullCount = thread->unreadMentions().count(); + const auto loadedCount = thread->unreadMentions().loadedCount(); const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount); if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) { - requestMentions(entry, loadedCount); + requestMentions(thread, loadedCount); } } -void UnreadThings::preloadEnoughReactions(not_null entry) { - const auto fullCount = entry->unreadReactions().count(); - const auto loadedCount = entry->unreadReactions().loadedCount(); +void UnreadThings::preloadEnoughReactions(not_null thread) { + const auto fullCount = thread->unreadReactions().count(); + const auto loadedCount = thread->unreadReactions().loadedCount(); const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount); if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) { - requestReactions(entry, loadedCount); + requestReactions(thread, loadedCount); } } -void UnreadThings::cancelRequests(not_null entry) { - if (const auto requestId = _mentionsRequests.take(entry)) { +void UnreadThings::cancelRequests(not_null thread) { + if (const auto requestId = _mentionsRequests.take(thread)) { _api->request(*requestId).cancel(); } - if (const auto requestId = _reactionsRequests.take(entry)) { + if (const auto requestId = _reactionsRequests.take(thread)) { _api->request(*requestId).cancel(); } } void UnreadThings::requestMentions( - not_null entry, + not_null thread, int loaded) { - if (_mentionsRequests.contains(entry)) { + if (_mentionsRequests.contains(thread)) { return; } const auto offsetId = std::max( - entry->unreadMentions().maxLoaded(), + thread->unreadMentions().maxLoaded(), MsgId(1)); const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit; const auto addOffset = loaded ? -(limit + 1) : -limit; const auto maxId = 0; const auto minId = 0; - const auto history = ResolveHistory(entry); - const auto topic = entry->asTopic(); + const auto history = thread->owningHistory(); + const auto topic = thread->asTopic(); using Flag = MTPmessages_GetUnreadMentions::Flag; const auto requestId = _api->request(MTPmessages_GetUnreadMentions( MTP_flags(topic ? Flag::f_top_msg_id : Flag()), @@ -128,29 +116,29 @@ void UnreadThings::requestMentions( MTP_int(maxId), MTP_int(minId) )).done([=](const MTPmessages_Messages &result) { - _mentionsRequests.remove(entry); - entry->unreadMentions().addSlice(result, loaded); + _mentionsRequests.remove(thread); + thread->unreadMentions().addSlice(result, loaded); }).fail([=] { - _mentionsRequests.remove(entry); + _mentionsRequests.remove(thread); }).send(); - _mentionsRequests.emplace(entry, requestId); + _mentionsRequests.emplace(thread, requestId); } void UnreadThings::requestReactions( - not_null entry, + not_null thread, int loaded) { - if (_reactionsRequests.contains(entry)) { + if (_reactionsRequests.contains(thread)) { return; } const auto offsetId = loaded - ? std::max(entry->unreadReactions().maxLoaded(), MsgId(1)) + ? std::max(thread->unreadReactions().maxLoaded(), MsgId(1)) : MsgId(1); const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit; const auto addOffset = loaded ? -(limit + 1) : -limit; const auto maxId = 0; const auto minId = 0; - const auto history = ResolveHistory(entry); - const auto topic = entry->asTopic(); + const auto history = thread->owningHistory(); + const auto topic = thread->asTopic(); using Flag = MTPmessages_GetUnreadReactions::Flag; const auto requestId = _api->request(MTPmessages_GetUnreadReactions( MTP_flags(topic ? Flag::f_top_msg_id : Flag()), @@ -162,12 +150,12 @@ void UnreadThings::requestReactions( MTP_int(maxId), MTP_int(minId) )).done([=](const MTPmessages_Messages &result) { - _reactionsRequests.remove(entry); - entry->unreadReactions().addSlice(result, loaded); + _reactionsRequests.remove(thread); + thread->unreadReactions().addSlice(result, loaded); }).fail([=] { - _reactionsRequests.remove(entry); + _reactionsRequests.remove(thread); }).send(); - _reactionsRequests.emplace(entry, requestId); + _reactionsRequests.emplace(thread, requestId); } } // namespace UnreadThings diff --git a/Telegram/SourceFiles/api/api_unread_things.h b/Telegram/SourceFiles/api/api_unread_things.h index f02b86368..f3c7e1711 100644 --- a/Telegram/SourceFiles/api/api_unread_things.h +++ b/Telegram/SourceFiles/api/api_unread_things.h @@ -11,40 +11,38 @@ class ApiWrap; class PeerData; class ChannelData; -namespace Dialogs { -class Entry; -} // namespace Dialogs +namespace Data { +class Thread; +} // namespace Data namespace Api { class UnreadThings final { public: - using DialogsEntry = Dialogs::Entry; - explicit UnreadThings(not_null api); - [[nodiscard]] bool trackMentions(DialogsEntry *entry) const; - [[nodiscard]] bool trackReactions(DialogsEntry *entry) const; + [[nodiscard]] bool trackMentions(Data::Thread *thread) const; + [[nodiscard]] bool trackReactions(Data::Thread *thread) const; - void preloadEnough(DialogsEntry *entry); + void preloadEnough(Data::Thread *thread); void mediaAndMentionsRead( const base::flat_set &readIds, ChannelData *channel = nullptr); - void cancelRequests(not_null entry); + void cancelRequests(not_null thread); private: - void preloadEnoughMentions(not_null entry); - void preloadEnoughReactions(not_null entry); + void preloadEnoughMentions(not_null thread); + void preloadEnoughReactions(not_null thread); - void requestMentions(not_null entry, int loaded); - void requestReactions(not_null entry, int loaded); + void requestMentions(not_null thread, int loaded); + void requestReactions(not_null thread, int loaded); const not_null _api; - base::flat_map, mtpRequestId> _mentionsRequests; - base::flat_map, mtpRequestId> _reactionsRequests; + base::flat_map, mtpRequestId> _mentionsRequests; + base::flat_map, mtpRequestId> _reactionsRequests; }; diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index ae9737e66..0badcd675 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -130,7 +130,7 @@ void Forum::applyReceivedTopics( const auto raw = creating ? _topics.emplace( rootId, - std::make_unique(_history, rootId) + std::make_unique(this, rootId) ).first->second.get() : i->second.get(); raw->applyTopic(topic); @@ -194,7 +194,7 @@ void Forum::applyTopicAdded( ? i->second.get() : _topics.emplace( rootId, - std::make_unique(_history, rootId) + std::make_unique(this, rootId) ).first->second.get(); raw->applyTitle(title); raw->applyColorId(colorId); @@ -262,11 +262,13 @@ void Forum::clearAllUnreadReactions() { } ForumTopic *Forum::topicFor(not_null item) { - const auto maybe = topicFor(item->replyToTop()); - return maybe ? maybe : topicFor(item->topicRootId()); + return topicFor(item->topicRootId()); } ForumTopic *Forum::topicFor(MsgId rootId) { + if (!rootId) { + return nullptr; + } const auto i = _topics.find(rootId); return (i != end(_topics)) ? i->second.get() : nullptr; } diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index fb9463e0f..8fa2d05c3 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -137,13 +137,14 @@ QImage ForumTopicIconFrame( return background; } -ForumTopic::ForumTopic(not_null history, MsgId rootId) -: Entry(&history->owner(), Type::ForumTopic) -, _forum(history->peer->forum()) +ForumTopic::ForumTopic(not_null forum, MsgId rootId) +: Thread(&forum->history()->owner(), Type::ForumTopic) +, _forum(forum) , _list(_forum->topicsList()) -, _replies(std::make_shared(history, rootId)) -, _rootId(rootId) -, _flags(owner().notifySettings().isMuted(this) ? Flag::Muted : Flag(0)) { +, _replies(std::make_shared(history(), rootId)) +, _rootId(rootId) { + Thread::setMuted(owner().notifySettings().isMuted(this)); + _replies->unreadCountValue( ) | rpl::combine_previous( ) | rpl::filter([=] { @@ -486,13 +487,9 @@ int ForumTopic::unreadCountForBadge() const { return (!result && unreadMark()) ? 1 : result; } -bool ForumTopic::muted() const { - return (_flags & Flag::Muted); -} - -bool ForumTopic::changeMuted(bool muted) { +void ForumTopic::setMuted(bool muted) { if (this->muted() == muted) { - return false; + return; } const auto refresher = gsl::finally([&] { if (inChatList()) { @@ -504,12 +501,7 @@ bool ForumTopic::changeMuted(bool muted) { }); const auto notify = (unreadCountForBadge() > 0); const auto notifier = unreadStateChangeNotifier(notify); - if (muted) { - _flags |= Flag::Muted; - } else { - _flags &= ~Flag::Muted; - } - return true; + Thread::setMuted(muted); } bool ForumTopic::unreadCountKnown() const { @@ -528,15 +520,7 @@ void ForumTopic::setUnreadMark(bool unread) { session().changes().topicUpdated(this, UpdateFlag::UnreadView); }); const auto notifier = unreadStateChangeNotifier(noUnreadMessages); - if (unread) { - _flags |= Flag::UnreadMark; - } else { - _flags &= ~Flag::UnreadMark; - } -} - -bool ForumTopic::unreadMark() const { - return (_flags & Flag::UnreadMark); + Thread::setUnreadMark(unread); } int ForumTopic::chatListUnreadCount() const { diff --git a/Telegram/SourceFiles/data/data_forum_topic.h b/Telegram/SourceFiles/data/data_forum_topic.h index 8ee4a99df..bc1d3ebac 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.h +++ b/Telegram/SourceFiles/data/data_forum_topic.h @@ -7,8 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "dialogs/dialogs_entry.h" -#include "dialogs/ui/dialogs_message_view.h" +#include "data/data_thread.h" #include "data/notify/data_peer_notify_settings.h" #include "base/flags.h" @@ -42,13 +41,14 @@ class Forum; const QString &title, const style::ForumTopicIcon &st); -class ForumTopic final : public Dialogs::Entry { +class ForumTopic final : public Data::Thread { public: - ForumTopic(not_null history, MsgId rootId); + ForumTopic(not_null forum, MsgId rootId); ~ForumTopic(); - ForumTopic(const ForumTopic &) = delete; - ForumTopic &operator=(const ForumTopic &) = delete; + not_null owningHistory() override { + return history(); + } [[nodiscard]] std::shared_ptr replies() const; [[nodiscard]] not_null channel() const; @@ -108,22 +108,11 @@ public: [[nodiscard]] bool unreadCountKnown() const; [[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0. - [[nodiscard]] bool muted() const; - bool changeMuted(bool muted); - void setUnreadMark(bool unread); - [[nodiscard]] bool unreadMark() const; - - Ui::Text::String cloudDraftTextCache; - Dialogs::Ui::MessageView lastItemDialogsView; + void setMuted(bool muted) override; + void setUnreadMark(bool unread) override; private: - enum class Flag : uchar { - UnreadMark = (1 << 0), - Muted = (1 << 1), - }; - friend inline constexpr bool is_flag_type(Flag) { return true; } - void indexTitleParts(); void validateDefaultIcon() const; void applyTopicTopMessage(MsgId topMessageId); @@ -159,7 +148,6 @@ private: std::optional _lastServerMessage; std::optional _chatListMessage; base::flat_set _requestedGroups; - base::flags _flags; // Initializer accesses _notify, be careful. rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 14663a5b9..805440472 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -901,14 +901,18 @@ Data::Forum *PeerData::forum() const { Data::ForumTopic *PeerData::forumTopicFor( not_null item) const { - if (const auto forum = this->forum()) { - return forum->topicFor(item); + if (const auto rootId = item->topicRootId()) { + if (const auto forum = this->forum()) { + return forum->topicFor(rootId); + } } return nullptr; } Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const { - if (const auto forum = this->forum()) { + if (!rootId) { + return nullptr; + } else if (const auto forum = this->forum()) { return forum->topicFor(rootId); } return nullptr; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index d6fcce1e7..f5dadc374 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1496,9 +1496,14 @@ void Session::requestItemRepaint(not_null item) { } } const auto history = item->history(); - if (history->lastItemDialogsView.dependsOn(item)) { + if (history->lastItemDialogsView().dependsOn(item)) { history->updateChatListEntry(); } + if (const auto topic = item->topic()) { + if (topic->lastItemDialogsView().dependsOn(item)) { + topic->updateChatListEntry(); + } + } } rpl::producer> Session::itemRepaintRequest() const { diff --git a/Telegram/SourceFiles/data/data_thread.cpp b/Telegram/SourceFiles/data/data_thread.cpp new file mode 100644 index 000000000..a35ac1d8d --- /dev/null +++ b/Telegram/SourceFiles/data/data_thread.cpp @@ -0,0 +1,133 @@ +/* +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_thread.h" + +#include "history/history.h" +#include "history/history_item.h" +#include "history/history_unread_things.h" + +namespace Data { + +Thread::~Thread() = default; + +void Thread::setUnreadThingsKnown() { + _flags |= Flag::UnreadThingsKnown; +} + +HistoryUnreadThings::Proxy Thread::unreadMentions() { + return { + this, + _unreadThings, + HistoryUnreadThings::Type::Mentions, + !!(_flags & Flag::UnreadThingsKnown), + }; +} + +HistoryUnreadThings::ConstProxy Thread::unreadMentions() const { + return { + _unreadThings ? &_unreadThings->mentions : nullptr, + !!(_flags & Flag::UnreadThingsKnown), + }; +} + +HistoryUnreadThings::Proxy Thread::unreadReactions() { + return { + this, + _unreadThings, + HistoryUnreadThings::Type::Reactions, + !!(_flags & Flag::UnreadThingsKnown), + }; +} + +HistoryUnreadThings::ConstProxy Thread::unreadReactions() const { + return { + _unreadThings ? &_unreadThings->reactions : nullptr, + !!(_flags & Flag::UnreadThingsKnown), + }; +} + +const base::flat_set &Thread::unreadMentionsIds() const { + if (!_unreadThings) { + static const auto Result = base::flat_set(); + return Result; + } + return _unreadThings->mentions.ids(); +} + +const base::flat_set &Thread::unreadReactionsIds() const { + if (!_unreadThings) { + static const auto Result = base::flat_set(); + return Result; + } + return _unreadThings->reactions.ids(); +} + +void Thread::clearNotifications() { + _notifications.clear(); +} + +void Thread::clearIncomingNotifications() { + if (!owningHistory()->peer->isSelf()) { + const auto proj = [](ItemNotification notification) { + return notification.item->out(); + }; + _notifications.erase( + ranges::remove(_notifications, false, proj), + end(_notifications)); + } +} + +void Thread::removeNotification(not_null item) { + _notifications.erase( + ranges::remove(_notifications, item, &ItemNotification::item), + end(_notifications)); +} + +std::optional Thread::currentNotification() const { + return empty(_notifications) + ? std::nullopt + : std::make_optional(_notifications.front()); +} + +bool Thread::hasNotification() const { + return !empty(_notifications); +} + +void Thread::skipNotification() { + if (!empty(_notifications)) { + _notifications.pop_front(); + } +} + +void Thread::pushNotification(ItemNotification notification) { + _notifications.push_back(notification); +} + +void Thread::popNotification(ItemNotification notification) { + if (!empty(_notifications) && (_notifications.back() == notification)) { + _notifications.pop_back(); + } +} + +void Thread::setMuted(bool muted) { + if (muted) { + _flags |= Flag::Muted; + } else { + _flags &= ~Flag::Muted; + } +} + +void Thread::setUnreadMark(bool unread) { + if (unread) { + _flags |= Flag::UnreadMark; + } else { + _flags &= ~Flag::UnreadMark; + } +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_thread.h b/Telegram/SourceFiles/data/data_thread.h new file mode 100644 index 000000000..80f5d0991 --- /dev/null +++ b/Telegram/SourceFiles/data/data_thread.h @@ -0,0 +1,112 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "dialogs/dialogs_entry.h" +#include "dialogs/ui/dialogs_message_view.h" +#include "ui/text/text.h" + +#include + +namespace Main { +class Session; +} // namespace Main + +namespace HistoryUnreadThings { +enum class AddType; +struct All; +class Proxy; +class ConstProxy; +} // namespace HistoryUnreadThings + +namespace st { +extern const int &dialogsTextWidthMin; +} // namespace st + +namespace Data { + +enum class ItemNotificationType { + Message, + Reaction, +}; + +struct ItemNotification { + not_null item; + UserData *reactionSender = nullptr; + ItemNotificationType type = ItemNotificationType::Message; + + friend inline auto operator<=>( + ItemNotification a, + ItemNotification b) = default; +}; + +class Thread : public Dialogs::Entry { +public: + using Entry::Entry; + ~Thread(); + + [[nodiscard]] virtual not_null owningHistory() = 0; + + [[nodiscard]] not_null owningHistory() const { + return const_cast(this)->owningHistory(); + } + + void setUnreadThingsKnown(); + [[nodiscard]] HistoryUnreadThings::Proxy unreadMentions(); + [[nodiscard]] HistoryUnreadThings::ConstProxy unreadMentions() const; + [[nodiscard]] HistoryUnreadThings::Proxy unreadReactions(); + [[nodiscard]] HistoryUnreadThings::ConstProxy unreadReactions() const; + + void removeNotification(not_null item); + void clearNotifications(); + void clearIncomingNotifications(); + [[nodiscard]] auto currentNotification() const + -> std::optional; + bool hasNotification() const; + void skipNotification(); + void pushNotification(ItemNotification notification); + void popNotification(ItemNotification notification); + + [[nodiscard]] bool muted() const { + return (_flags & Flag::Muted); + } + virtual void setMuted(bool muted); + + [[nodiscard]] bool unreadMark() const { + return (_flags & Flag::UnreadMark); + } + virtual void setUnreadMark(bool unread); + + [[nodiscard]] const base::flat_set &unreadMentionsIds() const; + [[nodiscard]] const base::flat_set &unreadReactionsIds() const; + + [[nodiscard]] Ui::Text::String &cloudDraftTextCache() { + return _cloudDraftTextCache; + } + [[nodiscard]] Dialogs::Ui::MessageView &lastItemDialogsView() { + return _lastItemDialogsView; + } + +private: + enum class Flag : uchar { + UnreadMark = (1 << 0), + Muted = (1 << 1), + UnreadThingsKnown = (1 << 2), + }; + friend inline constexpr bool is_flag_type(Flag) { return true; } + + Ui::Text::String _cloudDraftTextCache = { st::dialogsTextWidthMin }; + Dialogs::Ui::MessageView _lastItemDialogsView; + std::unique_ptr _unreadThings; + std::deque _notifications; + + base::flags _flags; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp index ea4982c77..335d2dfa7 100644 --- a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp +++ b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp @@ -189,7 +189,7 @@ void NotifySettings::defaultUpdate( void NotifySettings::updateLocal(not_null topic) { auto changesIn = crl::time(0); const auto muted = isMuted(topic, &changesIn); - topic->changeMuted(muted); + topic->setMuted(muted); if (muted) { auto &lifetime = _mutedTopics.emplace( topic, @@ -209,7 +209,9 @@ void NotifySettings::updateLocal(not_null peer) { const auto history = _owner->historyLoaded(peer->id); auto changesIn = crl::time(0); const auto muted = isMuted(peer, &changesIn); - if (history && history->changeMuted(muted)) { + const auto changeInHistory = history && (history->muted() != muted); + if (changeInHistory) { + history->setMuted(muted); // Notification already sent. } else { peer->session().changes().peerUpdated( @@ -316,18 +318,15 @@ void NotifySettings::unmuteByFinished() { const auto history = _owner->historyLoaded((*i)->id); auto changesIn = crl::time(0); const auto muted = isMuted(*i, &changesIn); + if (history) { + history->setMuted(muted); + } if (muted) { - if (history) { - history->changeMuted(true); - } if (!changesInMin || changesInMin > changesIn) { changesInMin = changesIn; } ++i; } else { - if (history) { - history->changeMuted(false); - } i = _mutedPeers.erase(i); } } @@ -335,14 +334,13 @@ void NotifySettings::unmuteByFinished() { auto changesIn = crl::time(0); const auto topic = i->first; const auto muted = isMuted(topic, &changesIn); + topic->setMuted(muted); if (muted) { - topic->changeMuted(true); if (!changesInMin || changesInMin > changesIn) { changesInMin = changesIn; } ++i; } else { - topic->changeMuted(false); i = _mutedTopics.erase(i); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index af0320487..a0598c29a 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_options.h" #include "history/history.h" #include "history/history_item.h" -#include "history/history_unread_things.h" #include "styles/style_dialogs.h" // st::dialogsTextWidthMin namespace Dialogs { @@ -70,6 +69,12 @@ Data::Folder *Entry::asFolder() { : nullptr; } +Data::Thread *Entry::asThread() { + return (_type == Type::History || _type == Type::ForumTopic) + ? static_cast(this) + : nullptr; +} + Data::ForumTopic *Entry::asTopic() { return (_type == Type::ForumTopic) ? static_cast(this) @@ -104,41 +109,6 @@ void Entry::cachePinnedIndex(FilterId filterId, int index) { pinnedIndexChanged(filterId, was, index); } -void Entry::cacheTopPromoted(bool promoted) { - if (isTopPromoted() == promoted) { - return; - } else if (promoted) { - _flags |= Flag::IsTopPromoted; - } else { - _flags &= ~Flag::IsTopPromoted; - } - updateChatListSortPosition(); - updateChatListEntry(); - if (!isTopPromoted()) { - updateChatListExistence(); - } -} - -bool Entry::isTopPromoted() const { - return (_flags & Flag::IsTopPromoted); -} - -const base::flat_set &Entry::unreadMentionsIds() const { - if (!_unreadThings) { - static const auto Result = base::flat_set(); - return Result; - } - return _unreadThings->mentions.ids(); -} - -const base::flat_set &Entry::unreadReactionsIds() const { - if (!_unreadThings) { - static const auto Result = base::flat_set(); - return Result; - } - return _unreadThings->reactions.ids(); -} - bool Entry::needUpdateInChatList() const { return inChatList() || shouldBeInChatList(); } @@ -220,42 +190,6 @@ TimeId Entry::adjustedChatListTimeId() const { return chatListTimeId(); } -void Entry::setUnreadThingsKnown() { - _flags |= Flag::UnreadThingsKnown; -} - -HistoryUnreadThings::Proxy Entry::unreadMentions() { - return { - this, - _unreadThings, - HistoryUnreadThings::Type::Mentions, - !!(_flags & Flag::UnreadThingsKnown), - }; -} - -HistoryUnreadThings::ConstProxy Entry::unreadMentions() const { - return { - _unreadThings ? &_unreadThings->mentions : nullptr, - !!(_flags & Flag::UnreadThingsKnown), - }; -} - -HistoryUnreadThings::Proxy Entry::unreadReactions() { - return { - this, - _unreadThings, - HistoryUnreadThings::Type::Reactions, - !!(_flags & Flag::UnreadThingsKnown), - }; -} - -HistoryUnreadThings::ConstProxy Entry::unreadReactions() const { - return { - _unreadThings ? &_unreadThings->reactions : nullptr, - !!(_flags & Flag::UnreadThingsKnown), - }; -} - void Entry::changedChatListPinHook() { } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 8cc353d05..ae3c5f56f 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/dialogs_key.h" #include "ui/unread_badge.h" +class HistoryItem; +class UserData; + namespace Main { class Session; } // namespace Main @@ -23,13 +26,6 @@ class ForumTopic; class CloudImageView; } // namespace Data -namespace HistoryUnreadThings { -enum class AddType; -struct All; -class Proxy; -class ConstProxy; -} // namespace HistoryUnreadThings - namespace Ui { } // namespace Ui @@ -113,8 +109,6 @@ public: ForumTopic, }; Entry(not_null owner, Type type); - Entry(const Entry &other) = delete; - Entry &operator=(const Entry &other) = delete; virtual ~Entry(); [[nodiscard]] Data::Session &owner() const; @@ -122,6 +116,7 @@ public: History *asHistory(); Data::Folder *asFolder(); + Data::Thread *asThread(); Data::ForumTopic *asTopic(); PositionChange adjustByPosInChatList( @@ -149,7 +144,6 @@ public: return lookupPinnedIndex(filterId) != 0; } void cachePinnedIndex(FilterId filterId, int index); - [[nodiscard]] bool isTopPromoted() const; [[nodiscard]] uint64 sortKeyInChatList(FilterId filterId) const { return filterId ? computeSortPosition(filterId) @@ -161,12 +155,6 @@ public: bool needUpdateInChatList() const; virtual TimeId adjustedChatListTimeId() const; - void setUnreadThingsKnown(); - [[nodiscard]] HistoryUnreadThings::Proxy unreadMentions(); - [[nodiscard]] HistoryUnreadThings::ConstProxy unreadMentions() const; - [[nodiscard]] HistoryUnreadThings::Proxy unreadReactions(); - [[nodiscard]] HistoryUnreadThings::ConstProxy unreadReactions() const; - virtual int fixedOnTopIndex() const = 0; static constexpr auto kArchiveFixOnTopIndex = 1; static constexpr auto kTopPromotionFixOnTopIndex = 2; @@ -220,18 +208,7 @@ protected: [[nodiscard]] int lookupPinnedIndex(FilterId filterId) const; - void cacheTopPromoted(bool promoted); - - [[nodiscard]] const base::flat_set &unreadMentionsIds() const; - [[nodiscard]] const base::flat_set &unreadReactionsIds() const; - private: - enum class Flag : uchar { - IsTopPromoted = 0x01, - UnreadThingsKnown = 0x02, - }; - friend inline constexpr bool is_flag_type(Flag) { return true; } - virtual void changedChatListPinHook(); void pinnedIndexChanged(FilterId filterId, int was, int now); [[nodiscard]] uint64 computeSortPosition(FilterId filterId) const; @@ -247,12 +224,10 @@ private: uint64 _sortKeyInChatList = 0; uint64 _sortKeyByDate = 0; base::flat_map _pinnedIndex; - std::unique_ptr _unreadThings; mutable Ui::PeerBadge _chatListBadge; mutable Ui::Text::String _chatListNameText; mutable int _chatListNameVersion = 0; TimeId _timeId = 0; - base::flags _flags; const Type _type; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.cpp b/Telegram/SourceFiles/dialogs/dialogs_key.cpp index ecd9ad988..9a43dc8a1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_key.cpp @@ -12,12 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" namespace Dialogs { -namespace { - -using Folder = Data::Folder; -using ForumTopic = Data::ForumTopic; - -} // namespace Key::Key(History *history) : _value(history) { } @@ -47,14 +41,18 @@ History *Key::history() const { return _value ? _value->asHistory() : nullptr; } -Folder *Key::folder() const { +Data::Folder *Key::folder() const { return _value ? _value->asFolder() : nullptr; } -ForumTopic *Key::topic() const { +Data::ForumTopic *Key::topic() const { return _value ? _value->asTopic() : nullptr; } +Data::Thread *Key::thread() const { + return _value ? _value->asThread() : nullptr; +} + History *Key::parentHistory() const { if (const auto result = history()) { return result; diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.h b/Telegram/SourceFiles/dialogs/dialogs_key.h index 2bb5f2401..1e3bcb987 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.h +++ b/Telegram/SourceFiles/dialogs/dialogs_key.h @@ -11,6 +11,7 @@ class History; class PeerData; namespace Data { +class Thread; class Folder; class ForumTopic; } // namespace Data @@ -40,6 +41,7 @@ public: History *history() const; Data::Folder *folder() const; Data::ForumTopic *topic() const; + Data::Thread *thread() const; History *parentHistory() const; PeerData *peer() const; diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index 31f781fea..878f33c03 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -96,6 +96,9 @@ public: [[nodiscard]] Data::ForumTopic *topic() const { return _id.topic(); } + [[nodiscard]] Data::Thread *thread() const { + return _id.thread(); + } [[nodiscard]] not_null entry() const { return _id.entry(); } diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index 7d70fe021..4d886f556 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -35,7 +35,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_user.h" #include "data/data_folder.h" -#include "data/data_forum_topic.h" #include "data/data_peer_values.h" namespace Dialogs::Ui { @@ -346,7 +345,7 @@ void PaintRow( row->paintRipple(p, 0, 0, context.width, &ripple->c); const auto history = chat.history(); - const auto topic = chat.topic(); + const auto thread = chat.thread(); if (flags & Flag::SavedMessages) { EmptyUserpic::PaintSavedMessages( @@ -418,8 +417,8 @@ void PaintRow( if (promoted && !history->topPromotionMessage().isEmpty()) { auto availableWidth = namewidth; p.setFont(st::dialogsTextFont); - if (history->cloudDraftTextCache.isEmpty()) { - history->cloudDraftTextCache.setText( + if (history->cloudDraftTextCache().isEmpty()) { + history->cloudDraftTextCache().setText( st::dialogsTextStyle, history->topPromotionMessage(), DialogTextOptions()); @@ -429,7 +428,7 @@ void PaintRow( : context.selected ? st::dialogsTextFgOver : st::dialogsTextFg); - history->cloudDraftTextCache.draw(p, { + history->cloudDraftTextCache().draw(p, { .position = { nameleft, texttop }, .availableWidth = availableWidth, .spoiler = Text::DefaultSpoilerCache(), @@ -475,7 +474,7 @@ void PaintRow( context.width, color, context.paused)) { - if (history->cloudDraftTextCache.isEmpty()) { + if (history->cloudDraftTextCache().isEmpty()) { using namespace TextUtilities; auto draftWrapped = Text::PlainLink( tr::lng_dialogs_text_from_wrapped( @@ -500,7 +499,7 @@ void PaintRow( .session = &history->session(), .customEmojiRepaint = customEmojiRepaint, }; - history->cloudDraftTextCache.setMarkedText( + history->cloudDraftTextCache().setMarkedText( st::dialogsTextStyle, draftText, DialogTextOptions(), @@ -511,7 +510,7 @@ void PaintRow( : context.selected ? st::dialogsTextFgOver : st::dialogsTextFg); - history->cloudDraftTextCache.draw(p, { + history->cloudDraftTextCache().draw(p, { .position = { nameleft, texttop }, .availableWidth = availableWidth, .palette = &(supportMode @@ -562,7 +561,7 @@ void PaintRow( // Empty history } } else if (!item->isEmpty()) { - if ((history || topic) && !promoted) { + if (thread && !promoted) { PaintRowDate(p, date, rectForName, context); } @@ -886,7 +885,7 @@ void RowPainter::Paint( const PaintContext &context) { const auto entry = row->entry(); const auto history = row->history(); - const auto topic = row->topic(); + const auto thread = row->thread(); const auto peer = history ? history->peer.get() : nullptr; const auto unreadCount = entry->chatListUnreadCount(); const auto unreadMark = entry->chatListUnreadMark(); @@ -916,12 +915,11 @@ void RowPainter::Paint( ? base::unixtime::parse(cloudDraft->date) : QDateTime(); }(); - const auto displayMentionBadge = (history - && history->unreadMentions().has()) - || (topic && topic->unreadMentions().has()); + const auto displayMentionBadge = thread + && thread->unreadMentions().has(); const auto displayReactionBadge = !displayMentionBadge - && ((history && history->unreadReactions().has()) - || (topic && topic->unreadReactions().has())); + && thread + && thread->unreadReactions().has(); const auto mentionOrReactionMuted = (entry->folder() != nullptr) || (!displayMentionBadge && unreadMuted); const auto displayUnreadCounter = [&] { @@ -991,10 +989,8 @@ void RowPainter::Paint( : false; const auto view = actionWasPainted ? nullptr - : history - ? &history->lastItemDialogsView - : topic - ? &topic->lastItemDialogsView + : thread + ? &thread->lastItemDialogsView() : nullptr; if (const auto folder = row->folder()) { PaintListEntryText(p, row, context, rect); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 13edacfb7..aa9550553 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -70,13 +70,13 @@ using UpdateFlag = Data::HistoryUpdate::Flag; } // namespace History::History(not_null owner, PeerId peerId) -: Entry(owner, Type::History) +: Thread(owner, Type::History) , peer(owner->peer(peerId)) -, cloudDraftTextCache(st::dialogsTextWidthMin) , _delegateMixin(HistoryInner::DelegateMixin()) -, _flags(owner->notifySettings().isMuted(peer) ? Flag::Muted : Flag(0)) , _chatListNameSortKey(owner->nameSortKey(peer->name())) , _sendActionPainter(this) { + Thread::setMuted(owner->notifySettings().isMuted(peer)); + if (const auto user = peer->asUser()) { if (user->isBot()) { _outboxReadBefore = std::numeric_limits::max(); @@ -84,6 +84,8 @@ History::History(not_null owner, PeerId peerId) } } +History::~History() = default; + void History::clearLastKeyboard() { if (lastKeyboardId) { if (lastKeyboardId == lastKeyboardHiddenId) { @@ -100,38 +102,6 @@ int History::height() const { return _height; } -void History::removeNotification(not_null item) { - _notifications.erase( - ranges::remove(_notifications, item, &ItemNotification::item), - end(_notifications)); -} - -auto History::currentNotification() const -> std::optional { - return empty(_notifications) - ? std::nullopt - : std::make_optional(_notifications.front()); -} - -bool History::hasNotification() const { - return !empty(_notifications); -} - -void History::skipNotification() { - if (!empty(_notifications)) { - _notifications.pop_front(); - } -} - -void History::pushNotification(ItemNotification notification) { - _notifications.push_back(notification); -} - -void History::popNotification(ItemNotification notification) { - if (!empty(_notifications) && (_notifications.back() == notification)) { - _notifications.pop_back(); - } -} - bool History::hasPendingResizedItems() const { return _flags & Flag::HasPendingResizedItems; } @@ -182,7 +152,7 @@ void History::checkChatListMessageRemoved(not_null item) { } void History::itemVanished(not_null item) { - removeNotification(item); + item->thread()->removeNotification(item); if (lastKeyboardId == item->id) { clearLastKeyboard(); } @@ -255,7 +225,7 @@ void History::setDraft(Data::DraftKey key, std::unique_ptr &&draft) } const auto changingCloudDraft = (key == Data::DraftKey::Cloud()); if (changingCloudDraft) { - cloudDraftTextCache.clear(); + cloudDraftTextCache().clear(); } if (draft) { _drafts[key] = std::move(draft); @@ -283,7 +253,7 @@ void History::clearDrafts() { const auto changingCloudDraft = _drafts.contains(Data::DraftKey::Cloud()); _drafts.clear(); if (changingCloudDraft) { - cloudDraftTextCache.clear(); + cloudDraftTextCache().clear(); updateChatListSortPosition(); } } @@ -314,7 +284,7 @@ Data::Draft *History::createCloudDraft(const Data::Draft *fromDraft) { existing->date = base::unixtime::now(); } - cloudDraftTextCache.clear(); + cloudDraftTextCache().clear(); updateChatListSortPosition(); return cloudDraft(); @@ -1148,12 +1118,12 @@ void History::newItemAdded(not_null item) { from->madeAction(item->date()); } item->contributeToSlowmode(); - auto notification = ItemNotification{ + auto notification = Data::ItemNotification{ .item = item, - .type = ItemNotificationType::Message, + .type = Data::ItemNotificationType::Message, }; if (item->showNotification()) { - pushNotification(notification); + item->thread()->pushNotification(notification); } owner().notifyNewItemAdded(item); const auto stillShow = item->showNotification(); // Could be read already. @@ -1732,7 +1702,7 @@ void History::setUnreadMark(bool unread) { if (clearUnreadOnClientSide()) { unread = false; } - if (_unreadMark == unread) { + if (unreadMark() == unread) { return; } const auto noUnreadMessages = !unreadCount(); @@ -1744,11 +1714,7 @@ void History::setUnreadMark(bool unread) { session().changes().historyUpdated(this, UpdateFlag::UnreadView); }); const auto notifier = unreadStateChangeNotifier(noUnreadMessages); - _unreadMark = unread; -} - -bool History::unreadMark() const { - return _unreadMark; + Thread::setUnreadMark(unread); } void History::setFakeUnreadWhileOpened(bool enabled) { @@ -1768,13 +1734,9 @@ void History::setFakeUnreadWhileOpened(bool enabled) { return _fakeUnreadWhileOpened; } -bool History::muted() const { - return (_flags & Flag::Muted); -} - -bool History::changeMuted(bool muted) { +void History::setMuted(bool muted) { if (this->muted() == muted) { - return false; + return; } const auto refresher = gsl::finally([&] { if (inChatList()) { @@ -1787,12 +1749,7 @@ bool History::changeMuted(bool muted) { }); const auto notify = (unreadCountForBadge() > 0); const auto notifier = unreadStateChangeNotifier(notify); - if (muted) { - _flags |= Flag::Muted; - } else { - _flags &= ~Flag::Muted; - } - return true; + Thread::setMuted(muted); } void History::getNextFirstUnreadMessage() { @@ -2082,7 +2039,7 @@ bool History::chatListMutedBadge() const { Dialogs::UnreadState History::chatListUnreadState() const { auto result = Dialogs::UnreadState(); const auto count = _unreadCount.value_or(0); - const auto mark = !count && _unreadMark; + const auto mark = !count && unreadMark(); const auto muted = this->muted(); result.messages = count; result.messagesMuted = muted ? count : 0; @@ -2164,21 +2121,6 @@ void History::finishBuildingFrontBlock() { } } -void History::clearNotifications() { - _notifications.clear(); -} - -void History::clearIncomingNotifications() { - if (!peer->isSelf()) { - const auto proj = [](ItemNotification notification) { - return notification.item->out(); - }; - _notifications.erase( - ranges::remove(_notifications, false, proj), - end(_notifications)); - } -} - bool History::loadedAtBottom() const { return _loadedAtBottom; } @@ -2697,9 +2639,9 @@ void History::cacheTopPromotion( if (topPromotionType() != type || _topPromotedMessage != message) { _topPromotedType = type; _topPromotedMessage = message; - cloudDraftTextCache.clear(); + cloudDraftTextCache().clear(); } else if (changed) { - cloudDraftTextCache.clear(); + cloudDraftTextCache().clear(); } } @@ -3169,7 +3111,7 @@ void History::clear(ClearType type) { for (const auto &item : local) { item->destroy(); } - _notifications.clear(); + clearNotifications(); owner().notifyHistoryCleared(this); if (unreadCountKnown()) { setUnreadCount(0); @@ -3265,7 +3207,24 @@ void History::setHasPinnedMessages(bool has) { session().changes().historyUpdated(this, UpdateFlag::PinnedMessages); } -History::~History() = default; +void History::cacheTopPromoted(bool promoted) { + if (isTopPromoted() == promoted) { + return; + } else if (promoted) { + _flags |= Flag::IsTopPromoted; + } else { + _flags &= ~Flag::IsTopPromoted; + } + updateChatListSortPosition(); + updateChatListEntry(); + if (!isTopPromoted()) { + updateChatListExistence(); + } +} + +bool History::isTopPromoted() const { + return (_flags & Flag::IsTopPromoted); +} HistoryBlock::HistoryBlock(not_null history) : _history(history) { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 3fb2617fb..e495a992b 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -10,8 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_types.h" #include "data/data_peer.h" #include "data/data_drafts.h" -#include "dialogs/dialogs_entry.h" -#include "dialogs/ui/dialogs_message_view.h" +#include "data/data_thread.h" #include "history/view/history_view_send_action.h" #include "base/observer.h" #include "base/timer.h" @@ -71,31 +70,17 @@ enum class NewMessageType { Existing, }; -enum class ItemNotificationType { - Message, - Reaction, -}; -struct ItemNotification { - not_null item; - UserData *reactionSender = nullptr; - ItemNotificationType type = ItemNotificationType::Message; - - friend inline bool operator==(ItemNotification a, ItemNotification b) { - return (a.item == b.item) - && (a.reactionSender == b.reactionSender) - && (a.type == b.type); - } -}; - -class History final : public Dialogs::Entry { +class History final : public Data::Thread { public: using Element = HistoryView::Element; History(not_null owner, PeerId peerId); - History(const History &) = delete; - History &operator=(const History &) = delete; ~History(); + not_null owningHistory() override { + return this; + } + [[nodiscard]] auto delegateMixin() const -> not_null { return _delegateMixin.get(); @@ -257,21 +242,17 @@ public: [[nodiscard]] bool unreadCountRefreshNeeded(MsgId readTillId) const; void setUnreadCount(int newUnreadCount); - void setUnreadMark(bool unread); - [[nodiscard]] bool unreadMark() const; + void setUnreadMark(bool unread) override; void setFakeUnreadWhileOpened(bool enabled); [[nodiscard]] bool fakeUnreadWhileOpened() const; [[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0. - [[nodiscard]] bool muted() const; - bool changeMuted(bool muted); + void setMuted(bool muted) override; void addUnreadBar(); void destroyUnreadBar(); [[nodiscard]] Element *unreadBar() const; void calculateFirstUnreadMessage(); void unsetFirstUnreadMessage(); [[nodiscard]] Element *firstUnreadMessage() const; - void clearNotifications(); - void clearIncomingNotifications(); [[nodiscard]] bool loadedAtBottom() const; // last message is in the list void setNotLoadedAtBottom(); @@ -315,12 +296,6 @@ public: void itemRemoved(not_null item); void itemVanished(not_null item); - [[nodiscard]] std::optional currentNotification() const; - bool hasNotification() const; - void skipNotification(); - void pushNotification(ItemNotification notification); - void popNotification(ItemNotification notification); - bool hasPendingResizedItems() const; void setHasPendingResizedItems(); @@ -441,11 +416,13 @@ public: [[nodiscard]] bool hasPinnedMessages() const; void setHasPinnedMessages(bool has); + [[nodiscard]] bool isTopPromoted() const; + + const not_null peer; + // Still public data. std::deque> blocks; - not_null peer; - // we save the last showAtMsgId to restore the state when switching // between different conversation histories MsgId showAtMsgId = ShowAtUnreadMsgId; @@ -464,21 +441,20 @@ public: mtpRequestId sendRequestId = 0; - Ui::Text::String cloudDraftTextCache; - Dialogs::Ui::MessageView lastItemDialogsView; - private: friend class HistoryBlock; enum class Flag : uchar { HasPendingResizedItems = (1 << 0), - Muted = (1 << 1), + IsTopPromoted = (1 << 1), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; }; + void cacheTopPromoted(bool promoted); + // when this item is destroyed scrollTopItem just points to the next one // and scrollTopOffset remains the same // if we are at the bottom of the window scrollTopItem == nullptr and @@ -530,7 +506,6 @@ private: void mainViewRemoved( not_null block, not_null view); - void removeNotification(not_null item); TimeId adjustedChatListTimeId() const override; void changedChatListPinHook() override; @@ -614,7 +589,6 @@ private: QString _chatListNameSortKey; - bool _unreadMark = false; bool _fakeUnreadWhileOpened = false; bool _hasPinnedMessages = false; @@ -637,8 +611,6 @@ private: HistoryView::SendActionPainter _sendActionPainter; - std::deque _notifications; - }; class HistoryBlock { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index a0d3aeb08..299a75c52 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2746,6 +2746,7 @@ void HistoryInner::checkHistoryActivation() { adjustCurrent(_visibleAreaBottom); if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) { // Clear possible message notifications. + // Side-effect: Also clears all notifications from forum topics. Core::App().notifications().clearFromHistory(_history); } if (_curHistory != _history || _history->isEmpty()) { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 675c4cc87..336d87736 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -227,12 +227,12 @@ void CheckReactionNotificationSchedule( if (user->blockStatus() == Status::Unknown) { user->updateFull(); } - const auto notification = ItemNotification{ + const auto notification = Data::ItemNotification{ .item = item, .reactionSender = user, - .type = ItemNotificationType::Reaction, + .type = Data::ItemNotificationType::Reaction, }; - item->history()->pushNotification(notification); + item->thread()->pushNotification(notification); Core::App().notifications().schedule(notification); return; } @@ -374,9 +374,9 @@ void HistoryItem::invalidateChatListEntry() { history()->session().changes().messageUpdated( this, Data::MessageUpdate::Flag::DialogRowRefresh); - history()->lastItemDialogsView.itemInvalidated(this); + history()->lastItemDialogsView().itemInvalidated(this); if (const auto topic = this->topic()) { - topic->lastItemDialogsView.itemInvalidated(this); + topic->lastItemDialogsView().itemInvalidated(this); } } @@ -620,9 +620,20 @@ void HistoryItem::destroy() { _history->destroyMessage(this); } +not_null HistoryItem::thread() const { + if (const auto topic = this->topic()) { + return topic; + } + return _history; +} + Data::ForumTopic *HistoryItem::topic() const { - const auto forum = _history->peer->forum(); - return forum ? forum->topicFor(this) : nullptr; + if (const auto rootId = topicRootId()) { + if (const auto forum = _history->peer->forum()) { + return forum->topicFor(rootId); + } + } + return nullptr; } void HistoryItem::refreshMainView() { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 544020879..953a14783 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -46,6 +46,7 @@ class Media; struct MessageReaction; class MessageReactions; class ForumTopic; +class Thread; } // namespace Data namespace Main { @@ -122,6 +123,7 @@ public: const QString &label, const TextWithEntities &content); + [[nodiscard]] not_null thread() const; [[nodiscard]] not_null history() const { return _history; } diff --git a/Telegram/SourceFiles/history/history_unread_things.cpp b/Telegram/SourceFiles/history/history_unread_things.cpp index 4ce2fc038..5bf42c4c8 100644 --- a/Telegram/SourceFiles/history/history_unread_things.cpp +++ b/Telegram/SourceFiles/history/history_unread_things.cpp @@ -42,7 +42,7 @@ template void Proxy::setCount(int count) { if (!_known) { - _entry->setUnreadThingsKnown(); + _thread->setUnreadThingsKnown(); } if (!_data) { if (!count) { @@ -69,16 +69,16 @@ void Proxy::setCount(int count) { const auto has = (count > 0); if (has != had) { if (_type == Type::Mentions) { - if (const auto history = _entry->asHistory()) { - _entry->owner().chatsFilters().refreshHistory(history); + if (const auto history = _thread->asHistory()) { + _thread->owner().chatsFilters().refreshHistory(history); } } - _entry->updateChatListEntry(); + _thread->updateChatListEntry(); } } bool Proxy::add(MsgId msgId, AddType type) { - if (const auto history = _entry->asHistory()) { + if (const auto history = _thread->asHistory()) { if (history->peer->isBroadcast()) { return false; } @@ -130,7 +130,7 @@ void Proxy::addSlice(const MTPmessages_Messages &slice, int alreadyLoaded) { if (!alreadyLoaded && _data) { resolveList().clear(); } - const auto history = resolveHistory(); + const auto history = _thread->owningHistory(); auto fullCount = slice.match([&]( const MTPDmessages_messagesNotModified &) { LOG(("API Error: received messages.messagesNotModified! " @@ -150,7 +150,7 @@ void Proxy::addSlice(const MTPmessages_Messages &slice, int alreadyLoaded) { return data.vcount().v; }); - auto &owner = _entry->owner(); + auto &owner = _thread->owner(); const auto messages = slice.match([&]( const MTPDmessages_messagesNotModified &) { LOG(("API Error: received messages.messagesNotModified! " @@ -203,7 +203,7 @@ void Proxy::checkAdd(MsgId msgId, bool resolved) { if (!list.loadedCount() || list.maxLoaded() <= msgId) { return; } - const auto history = resolveHistory(); + const auto history = _thread->owningHistory(); const auto peer = history->peer; const auto item = peer->owner().message(peer, msgId); if (item && item->hasUnreadReaction()) { @@ -216,11 +216,11 @@ void Proxy::checkAdd(MsgId msgId, bool resolved) { } void Proxy::notifyUpdated() { - if (const auto history = _entry->asHistory()) { + if (const auto history = _thread->asHistory()) { history->session().changes().historyUpdated( history, HistoryUpdateFlag(_type)); - } else if (const auto topic = _entry->asTopic()) { + } else if (const auto topic = _thread->asTopic()) { topic->session().changes().topicUpdated( topic, TopicUpdateFlag(_type)); @@ -245,9 +245,4 @@ List &Proxy::resolveList() { Unexpected("Unread things type in Proxy::resolveList."); } -not_null Proxy::resolveHistory() const { - const auto result = _entry->asHistory(); - return result ? not_null(result) : _entry->asTopic()->history(); -} - } // namespace HistoryUnreadThings diff --git a/Telegram/SourceFiles/history/history_unread_things.h b/Telegram/SourceFiles/history/history_unread_things.h index 431dd3d64..a8b874d11 100644 --- a/Telegram/SourceFiles/history/history_unread_things.h +++ b/Telegram/SourceFiles/history/history_unread_things.h @@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; -namespace Dialogs { -class Entry; +namespace Data { +class Thread; } // namespace Data namespace HistoryUnreadThings { @@ -108,7 +108,7 @@ private: class Proxy final : public ConstProxy { public: Proxy( - not_null entry, + not_null thread, std::unique_ptr &data, Type type, bool known) @@ -119,7 +119,7 @@ public: ? &data->mentions : &data->reactions), known) - , _entry(entry) + , _thread(thread) , _data(data) , _type(type) , _known(known) { @@ -138,9 +138,8 @@ private: void createData(); void notifyUpdated(); [[nodiscard]] List &resolveList(); - [[nodiscard]] not_null resolveHistory() const; - const not_null _entry; + const not_null _thread; std::unique_ptr &_data; Type _type = Type::Mentions; bool _known = false; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 153c86dc9..d9ed05694 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2865,6 +2865,7 @@ void HistoryWidget::newItemAdded(not_null item) { session().data().histories().readInboxOnNewMessage(item); // Also clear possible scheduled messages notifications. + // Side-effect: Also clears all notifications from forum topics. Core::App().notifications().clearFromHistory(_history); } } @@ -3954,7 +3955,7 @@ void HistoryWidget::cornerButtonsShowAtPosition( } } -Dialogs::Entry *HistoryWidget::cornerButtonsEntry() { +Data::Thread *HistoryWidget::cornerButtonsThread() { return _history; } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 6757a88cc..1c1a6876b 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -325,7 +325,7 @@ private: void cornerButtonsShowAtPosition( Data::MessagePosition position) override; - Dialogs::Entry *cornerButtonsEntry() override; + Data::Thread *cornerButtonsThread() override; FullMsgId cornerButtonsCurrentId() override; bool cornerButtonsIgnoreVisibility() override; std::optional cornerButtonsDownShown() override; diff --git a/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp b/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp index 7482a40e3..22034dcf2 100644 --- a/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp +++ b/Telegram/SourceFiles/history/view/history_view_corner_buttons.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/history_unread_things.h" -#include "dialogs/dialogs_entry.h" #include "main/main_session.h" #include "menu/menu_send.h" #include "apiwrap.h" @@ -56,10 +55,10 @@ CornerButtons::CornerButtons( filterScroll(_reactions); SendMenu::SetupUnreadMentionsMenu(_mentions.widget.data(), [=] { - return _delegate->cornerButtonsEntry(); + return _delegate->cornerButtonsThread(); }); SendMenu::SetupUnreadReactionsMenu(_reactions.widget.data(), [=] { - return _delegate->cornerButtonsEntry(); + return _delegate->cornerButtonsThread(); }); } @@ -86,14 +85,14 @@ void CornerButtons::mentionsClick() { if (!history) { return; } - const auto entry = _delegate->cornerButtonsEntry(); - const auto msgId = entry->unreadMentions().minLoaded(); + const auto thread = _delegate->cornerButtonsThread(); + const auto msgId = thread->unreadMentions().minLoaded(); const auto already = (_delegate->cornerButtonsCurrentId().msg == msgId); // Mark mention voice/video message as read. // See https://github.com/telegramdesktop/tdesktop/issues/5623 if (msgId && already) { - if (const auto item = entry->owner().message(history->peer, msgId)) { + if (const auto item = thread->owner().message(history->peer, msgId)) { if (const auto media = item->media()) { if (const auto document = media->document()) { if (!media->webpage() @@ -113,8 +112,8 @@ void CornerButtons::reactionsClick() { if (!history) { return; } - const auto entry = _delegate->cornerButtonsEntry(); - showAt(entry->unreadReactions().minLoaded()); + const auto thread = _delegate->cornerButtonsThread(); + showAt(thread->unreadReactions().minLoaded()); } void CornerButtons::clearReplyReturns() { @@ -135,10 +134,10 @@ void CornerButtons::setReplyReturns(QVector replyReturns) { } void CornerButtons::computeCurrentReplyReturn() { - const auto entry = _delegate->cornerButtonsEntry(); - _replyReturn = (!entry || _replyReturns.empty()) + const auto thread = _delegate->cornerButtonsThread(); + _replyReturn = (!thread || _replyReturns.empty()) ? nullptr - : entry->owner().message(_replyReturns.back()); + : thread->owner().message(_replyReturns.back()); } void CornerButtons::skipReplyReturn(FullMsgId id) { @@ -188,15 +187,8 @@ CornerButton &CornerButtons::buttonByType(Type type) { } History *CornerButtons::lookupHistory() const { - const auto entry = _delegate->cornerButtonsEntry(); - if (!entry) { - return nullptr; - } else if (const auto history = entry->asHistory()) { - return history; - } else if (const auto topic = entry->asTopic()) { - return topic->history(); - } - return nullptr; + const auto thread = _delegate->cornerButtonsThread(); + return thread ? thread->owningHistory().get() : nullptr; } void CornerButtons::showAt(MsgId id) { @@ -225,14 +217,14 @@ void CornerButtons::updateUnreadThingsVisibility() { if (_delegate->cornerButtonsIgnoreVisibility()) { return; } - const auto entry = _delegate->cornerButtonsEntry(); - if (!entry) { + const auto thread = _delegate->cornerButtonsThread(); + if (!thread) { updateVisibility(Type::Mentions, false); updateVisibility(Type::Reactions, false); return; } - auto &unreadThings = entry->session().api().unreadThings(); - unreadThings.preloadEnough(entry); + auto &unreadThings = thread->session().api().unreadThings(); + unreadThings.preloadEnough(thread); const auto updateWithCount = [&](Type type, int count) { updateVisibility( @@ -240,25 +232,25 @@ void CornerButtons::updateUnreadThingsVisibility() { (count > 0) && _delegate->cornerButtonsUnreadMayBeShown()); }; if (_delegate->cornerButtonsHas(Type::Mentions) - && unreadThings.trackMentions(entry)) { - if (const auto count = entry->unreadMentions().count(0)) { + && unreadThings.trackMentions(thread)) { + if (const auto count = thread->unreadMentions().count(0)) { _mentions.widget->setUnreadCount(count); } updateWithCount( Type::Mentions, - entry->unreadMentions().loadedCount()); + thread->unreadMentions().loadedCount()); } else { updateVisibility(Type::Mentions, false); } if (_delegate->cornerButtonsHas(Type::Reactions) - && unreadThings.trackReactions(entry)) { - if (const auto count = entry->unreadReactions().count(0)) { + && unreadThings.trackReactions(thread)) { + if (const auto count = thread->unreadReactions().count(0)) { _reactions.widget->setUnreadCount(count); } updateWithCount( Type::Reactions, - entry->unreadReactions().loadedCount()); + thread->unreadReactions().loadedCount()); } else { updateVisibility(Type::Reactions, false); } @@ -353,8 +345,8 @@ Fn CornerButtons::doneJumpFrom( return [=](bool found) { skipReplyReturn(targetId); if (originId) { - if (const auto entry = _delegate->cornerButtonsEntry()) { - if (const auto item = entry->owner().message(originId)) { + if (const auto thread = _delegate->cornerButtonsThread()) { + if (const auto item = thread->owner().message(originId)) { pushReplyReturn(item); } } diff --git a/Telegram/SourceFiles/history/view/history_view_corner_buttons.h b/Telegram/SourceFiles/history/view/history_view_corner_buttons.h index 6caa9994d..bab384fcd 100644 --- a/Telegram/SourceFiles/history/view/history_view_corner_buttons.h +++ b/Telegram/SourceFiles/history/view/history_view_corner_buttons.h @@ -22,12 +22,9 @@ class HistoryDownButton; namespace Data { struct MessagePosition; +class Thread; } // namespace Data -namespace Dialogs { -class Entry; -} // namespace Dialogs - namespace HistoryView { struct CornerButton { @@ -50,7 +47,7 @@ class CornerButtonsDelegate { public: virtual void cornerButtonsShowAtPosition( Data::MessagePosition position) = 0; - [[nodiscard]] virtual Dialogs::Entry *cornerButtonsEntry() = 0; + [[nodiscard]] virtual Data::Thread *cornerButtonsThread() = 0; [[nodiscard]] virtual FullMsgId cornerButtonsCurrentId() = 0; [[nodiscard]] virtual bool cornerButtonsIgnoreVisibility() = 0; [[nodiscard]] virtual std::optional cornerButtonsDownShown() = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp index 2ede8215e..8e4ff214f 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.cpp @@ -193,7 +193,7 @@ void PinnedWidget::cornerButtonsShowAtPosition( showAtPosition(position); } -Dialogs::Entry *PinnedWidget::cornerButtonsEntry() { +Data::Thread *PinnedWidget::cornerButtonsThread() { return _history; } diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_section.h b/Telegram/SourceFiles/history/view/history_view_pinned_section.h index be70e510a..b5bc978a1 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_section.h +++ b/Telegram/SourceFiles/history/view/history_view_pinned_section.h @@ -123,7 +123,7 @@ public: // CornerButtonsDelegate delegate. void cornerButtonsShowAtPosition( Data::MessagePosition position) override; - Dialogs::Entry *cornerButtonsEntry() override; + Data::Thread *cornerButtonsThread() override; FullMsgId cornerButtonsCurrentId() override; bool cornerButtonsIgnoreVisibility() override; std::optional cornerButtonsDownShown() override; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 659365fd0..aaa2cdada 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -1387,8 +1387,8 @@ void RepliesWidget::cornerButtonsShowAtPosition( showAtPosition(position); } -Dialogs::Entry *RepliesWidget::cornerButtonsEntry() { - return _topic ? static_cast(_topic) : _history; +Data::Thread *RepliesWidget::cornerButtonsThread() { + return _topic ? static_cast(_topic) : _history; } FullMsgId RepliesWidget::cornerButtonsCurrentId() { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 9bf999af5..9000c20d1 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -161,7 +161,7 @@ public: // CornerButtonsDelegate delegate. void cornerButtonsShowAtPosition( Data::MessagePosition position) override; - Dialogs::Entry *cornerButtonsEntry() override; + Data::Thread *cornerButtonsThread() override; FullMsgId cornerButtonsCurrentId() override; bool cornerButtonsIgnoreVisibility() override; std::optional cornerButtonsDownShown() override; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 18c081c0d..8338751a6 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -822,7 +822,7 @@ void ScheduledWidget::cornerButtonsShowAtPosition( showAtPosition(position); } -Dialogs::Entry *ScheduledWidget::cornerButtonsEntry() { +Data::Thread *ScheduledWidget::cornerButtonsThread() { return _history; } diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index c2767d638..f789f263c 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -146,7 +146,7 @@ public: // CornerButtonsDelegate delegate. void cornerButtonsShowAtPosition( Data::MessagePosition position) override; - Dialogs::Entry *cornerButtonsEntry() override; + Data::Thread *cornerButtonsThread() override; FullMsgId cornerButtonsCurrentId() override; bool cornerButtonsIgnoreVisibility() override; std::optional cornerButtonsDownShown() override; diff --git a/Telegram/SourceFiles/menu/menu_send.cpp b/Telegram/SourceFiles/menu/menu_send.cpp index 9262e406a..5c9240cee 100644 --- a/Telegram/SourceFiles/menu/menu_send.cpp +++ b/Telegram/SourceFiles/menu/menu_send.cpp @@ -152,29 +152,29 @@ void SetupMenuAndShortcuts( void SetupReadAllMenu( not_null button, - Fn currentEntry, + Fn currentThread, const QString &text, - Fn, Fn)> sendReadRequest) { + Fn, Fn)> sendReadRequest) { struct State { base::unique_qptr menu; - base::flat_set> sentForEntries; + base::flat_set> sentForEntries; }; const auto state = std::make_shared(); const auto showMenu = [=] { - const auto entry = base::make_weak(currentEntry()); - if (!entry) { + const auto thread = base::make_weak(currentThread()); + if (!thread) { return; } state->menu = base::make_unique_q( button, st::popupMenuWithIcons); state->menu->addAction(text, [=] { - const auto strong = entry.get(); - if (!strong || !state->sentForEntries.emplace(entry).second) { + const auto strong = thread.get(); + if (!strong || !state->sentForEntries.emplace(thread).second) { return; } sendReadRequest(strong, [=] { - state->sentForEntries.remove(entry); + state->sentForEntries.remove(thread); }); }, &st::menuIconMarkRead); state->menu->popup(QCursor::pos()); @@ -191,21 +191,19 @@ void SetupReadAllMenu( void SetupUnreadMentionsMenu( not_null button, - Fn currentEntry) { + Fn currentThread) { const auto text = tr::lng_context_mark_read_mentions_all(tr::now); const auto sendOne = [=]( - base::weak_ptr weakEntry, + base::weak_ptr weakThread, Fn done, auto resend) -> void { - const auto entry = weakEntry.get(); - if (!entry) { + const auto thread = weakThread.get(); + if (!thread) { done(); return; } - const auto history = entry->asHistory(); - const auto topic = entry->asTopic(); - Assert(history || topic); - const auto peer = (history ? history : topic->history().get())->peer; + const auto peer = thread->owningHistory()->peer; + const auto topic = thread->asTopic(); const auto rootId = topic ? topic->rootId() : 0; using Flag = MTPmessages_ReadMentions::Flag; peer->session().api().request(MTPmessages_ReadMentions( @@ -217,7 +215,7 @@ void SetupUnreadMentionsMenu( peer, result); if (offset > 0) { - resend(weakEntry, done, resend); + resend(weakThread, done, resend); } else { done(); peer->owner().history(peer)->clearUnreadMentionsFor(rootId); @@ -225,30 +223,28 @@ void SetupUnreadMentionsMenu( }).fail(done).send(); }; const auto sendRequest = [=]( - not_null entry, + not_null thread, Fn done) { - sendOne(base::make_weak(entry.get()), std::move(done), sendOne); + sendOne(base::make_weak(thread.get()), std::move(done), sendOne); }; - SetupReadAllMenu(button, currentEntry, text, sendRequest); + SetupReadAllMenu(button, currentThread, text, sendRequest); } void SetupUnreadReactionsMenu( not_null button, - Fn currentEntry) { + Fn currentThread) { const auto text = tr::lng_context_mark_read_reactions_all(tr::now); const auto sendOne = [=]( - base::weak_ptr weakEntry, + base::weak_ptr weakThread, Fn done, auto resend) -> void { - const auto entry = weakEntry.get(); - if (!entry) { + const auto thread = weakThread.get(); + if (!thread) { done(); return; } - const auto history = entry->asHistory(); - const auto topic = entry->asTopic(); - Assert(history || topic); - const auto peer = (history ? history : topic->history().get())->peer; + const auto topic = thread->asTopic(); + const auto peer = thread->owningHistory()->peer; const auto rootId = topic ? topic->rootId() : 0; using Flag = MTPmessages_ReadReactions::Flag; peer->session().api().request(MTPmessages_ReadReactions( @@ -260,7 +256,7 @@ void SetupUnreadReactionsMenu( peer, result); if (offset > 0) { - resend(weakEntry, done, resend); + resend(weakThread, done, resend); } else { done(); peer->owner().history(peer)->clearUnreadReactionsFor(rootId); @@ -268,11 +264,11 @@ void SetupUnreadReactionsMenu( }).fail(done).send(); }; const auto sendRequest = [=]( - not_null entry, + not_null thread, Fn done) { - sendOne(base::make_weak(entry.get()), std::move(done), sendOne); + sendOne(base::make_weak(thread.get()), std::move(done), sendOne); }; - SetupReadAllMenu(button, currentEntry, text, sendRequest); + SetupReadAllMenu(button, currentThread, text, sendRequest); } } // namespace SendMenu diff --git a/Telegram/SourceFiles/menu/menu_send.h b/Telegram/SourceFiles/menu/menu_send.h index 8aac254d8..2e401e768 100644 --- a/Telegram/SourceFiles/menu/menu_send.h +++ b/Telegram/SourceFiles/menu/menu_send.h @@ -16,9 +16,9 @@ class PopupMenu; class RpWidget; } // namespace Ui -namespace Dialogs { -class Entry; -} // namespace Dialogs +namespace Data { +class Thread; +} // namespace Data namespace SendMenu { @@ -55,10 +55,10 @@ void SetupMenuAndShortcuts( void SetupUnreadMentionsMenu( not_null button, - Fn currentEntry); + Fn currentThread); void SetupUnreadReactionsMenu( not_null button, - Fn currentEntry); + Fn currentThread); } // namespace SendMenu diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index a6a84c643..cfcd305fa 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -81,18 +81,18 @@ QString TextWithPermanentSpoiler(const TextWithEntities &textWithEntities) { struct System::Waiter { NotificationInHistoryKey key; UserData *reactionSender = nullptr; - ItemNotificationType type = ItemNotificationType::Message; + Data::ItemNotificationType type = Data::ItemNotificationType::Message; crl::time when = 0; }; System::NotificationInHistoryKey::NotificationInHistoryKey( - ItemNotification notification) + Data::ItemNotification notification) : NotificationInHistoryKey(notification.item->id, notification.type) { } System::NotificationInHistoryKey::NotificationInHistoryKey( MsgId messageId, - ItemNotificationType type) + Data::ItemNotificationType type) : messageId(messageId) , type(type) { } @@ -161,13 +161,13 @@ bool System::skipReactionNotification(not_null item) const { } System::SkipState System::skipNotification( - ItemNotification notification) const { + Data::ItemNotification notification) const { const auto item = notification.item; const auto type = notification.type; - const auto messageNotification = (type == ItemNotificationType::Message); - if (!item->history()->currentNotification() - || (messageNotification && item->skipNotification()) - || (type == ItemNotificationType::Reaction + const auto messageType = (type == Data::ItemNotificationType::Message); + if (!item->thread()->currentNotification() + || (messageType && item->skipNotification()) + || (type == Data::ItemNotificationType::Reaction && skipReactionNotification(item))) { return { SkipState::Skip }; } @@ -175,27 +175,27 @@ System::SkipState System::skipNotification( } System::SkipState System::computeSkipState( - ItemNotification notification) const { + Data::ItemNotification notification) const { const auto type = notification.type; const auto item = notification.item; const auto history = item->history(); - const auto messageNotification = (type == ItemNotificationType::Message); + const auto messageType = (type == Data::ItemNotificationType::Message); const auto withSilent = [&]( SkipState::Value value, bool forceSilent = false) { return SkipState{ .value = value, .silent = (forceSilent - || !messageNotification + || !messageType || item->isSilent() || history->owner().notifySettings().sound( history->peer).none), }; }; - const auto showForMuted = messageNotification + const auto showForMuted = messageType && item->out() && item->isFromScheduled(); - const auto notifyBy = messageNotification + const auto notifyBy = messageType ? item->specialNotificationPeer() : notification.reactionSender; if (Core::Quitting()) { @@ -205,7 +205,7 @@ System::SkipState System::computeSkipState( return { SkipState::Skip }; } - if (messageNotification) { + if (messageType) { history->owner().notifySettings().request( history->peer); } else if (notifyBy->blockStatus() == PeerData::BlockStatus::Unknown) { @@ -215,10 +215,10 @@ System::SkipState System::computeSkipState( history->owner().notifySettings().request(notifyBy); } - if (messageNotification + if (messageType && history->owner().notifySettings().muteUnknown(history->peer)) { return { SkipState::Unknown }; - } else if (messageNotification + } else if (messageType && !history->owner().notifySettings().isMuted(history->peer)) { return withSilent(SkipState::DontSkip); } else if (!notifyBy) { @@ -226,11 +226,11 @@ System::SkipState System::computeSkipState( showForMuted ? SkipState::DontSkip : SkipState::Skip, showForMuted); } else if (history->owner().notifySettings().muteUnknown(notifyBy) - || (!messageNotification + || (!messageType && notifyBy->blockStatus() == PeerData::BlockStatus::Unknown)) { return withSilent(SkipState::Unknown); } else if (!history->owner().notifySettings().isMuted(notifyBy) - && (messageNotification || !notifyBy->isBlocked())) { + && (messageType || !notifyBy->isBlocked())) { return withSilent(SkipState::DontSkip); } else { return withSilent( @@ -261,7 +261,7 @@ System::Timing System::countTiming( }; } -void System::schedule(ItemNotification notification) { +void System::schedule(Data::ItemNotification notification) { Expects(_manager != nullptr); const auto item = notification.item; @@ -275,13 +275,13 @@ void System::schedule(ItemNotification notification) { const auto ready = (skip.value != SkipState::Unknown) && item->notificationReady(); - const auto minimalDelay = (type == ItemNotificationType::Reaction) + const auto minimalDelay = (type == Data::ItemNotificationType::Reaction) ? kMinimalDelay : item->Has() ? kMinimalForwardDelay : kMinimalDelay; const auto timing = countTiming(history, minimalDelay); - const auto notifyBy = (type == ItemNotificationType::Message) + const auto notifyBy = (type == Data::ItemNotificationType::Message) ? item->specialNotificationPeer() : notification.reactionSender; if (!skip.silent) { @@ -320,8 +320,8 @@ void System::clearAll() { _manager->clearAll(); } - for (auto i = _whenMaps.cbegin(), e = _whenMaps.cend(); i != e; ++i) { - i->first->clearNotifications(); + for (const auto &[thread, _] : _whenMaps) { + thread->clearNotifications(); } _whenMaps.clear(); _whenAlerts.clear(); @@ -334,52 +334,33 @@ void System::clearFromTopic(not_null topic) { _manager->clearFromTopic(topic); } - // #TODO forum notifications - //topic->clearNotifications(); - //_whenMaps.remove(topic); - //_whenAlerts.remove(topic); - //_waiters.remove(topic); - //_settingWaiters.remove(topic); + topic->clearNotifications(); + _whenMaps.remove(topic); + _whenAlerts.remove(topic); + _waiters.remove(topic); + _settingWaiters.remove(topic); _waitTimer.cancel(); showNext(); } -void System::clearFromHistory(not_null history) { - if (_manager) { - _manager->clearFromHistory(history); - } - - history->clearNotifications(); - _whenMaps.remove(history); - _whenAlerts.remove(history); - _waiters.remove(history); - _settingWaiters.remove(history); - - _waitTimer.cancel(); - showNext(); -} - -void System::clearFromSession(not_null session) { - if (_manager) { - _manager->clearFromSession(session); - } - +void System::clearForThreadIf(Fn)> predicate) { for (auto i = _whenMaps.begin(); i != _whenMaps.end();) { - const auto history = i->first; - if (&history->session() != session) { + const auto thread = i->first; + if (!predicate(thread)) { ++i; continue; } - history->clearNotifications(); i = _whenMaps.erase(i); - _whenAlerts.remove(history); - _waiters.remove(history); - _settingWaiters.remove(history); + + thread->clearNotifications(); + _whenAlerts.remove(thread); + _waiters.remove(thread); + _settingWaiters.remove(thread); } const auto clearFrom = [&](auto &map) { for (auto i = map.begin(); i != map.end();) { - if (&i->first->session() == session) { + if (predicate(i->first)) { i = map.erase(i); } else { ++i; @@ -389,6 +370,27 @@ void System::clearFromSession(not_null session) { clearFrom(_whenAlerts); clearFrom(_waiters); clearFrom(_settingWaiters); + + _waitTimer.cancel(); + showNext(); +} + +void System::clearFromHistory(not_null history) { + if (_manager) { + _manager->clearFromHistory(history); + } + clearForThreadIf([&](not_null thread) { + return (thread->owningHistory() == history); + }); +} + +void System::clearFromSession(not_null session) { + if (_manager) { + _manager->clearFromSession(session); + } + clearForThreadIf([&](not_null thread) { + return (&thread->session() == session); + }); } void System::clearIncomingFromHistory(not_null history) { @@ -403,9 +405,8 @@ void System::clearIncomingFromTopic(not_null topic) { if (_manager) { _manager->clearFromTopic(topic); } - // #TODO forum notifications - //topic->clearIncomingNotifications(); - //_whenAlerts.remove(topic); + topic->clearIncomingNotifications(); + _whenAlerts.remove(topic); } void System::clearFromItem(not_null item) { @@ -428,10 +429,10 @@ void System::clearAllFast() { void System::checkDelayed() { for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) { const auto remove = [&] { - const auto history = i->first; - const auto peer = history->peer; + const auto thread = i->first; + const auto peer = thread->owningHistory()->peer; const auto fullId = FullMsgId(peer->id, i->second.key.messageId); - const auto item = peer->owner().message(fullId); + const auto item = thread->owner().message(fullId); if (!item) { return true; } @@ -503,7 +504,7 @@ void System::showNext() { auto alertPeer = (PeerData*)nullptr; for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) { while (!i->second.empty() && i->second.begin()->first <= ms) { - const auto peer = i->first->peer; + const auto peer = i->first->owningHistory()->peer; const auto ¬ifySettings = peer->owner().notifySettings(); const auto peerUnknown = notifySettings.muteUnknown(peer); const auto peerAlert = !peerUnknown @@ -559,15 +560,15 @@ void System::showNext() { while (true) { auto next = 0LL; - auto notify = std::optional(); - auto notifyHistory = (History*)nullptr; + auto notify = std::optional(); + auto notifyThread = (Data::Thread*)nullptr; for (auto i = _waiters.begin(); i != _waiters.end();) { - const auto history = i->first; - auto current = history->currentNotification(); + const auto thread = i->first; + auto current = thread->currentNotification(); if (current && current->item->id != i->second.key.messageId) { - auto j = _whenMaps.find(history); + auto j = _whenMaps.find(thread); if (j == _whenMaps.end()) { - history->clearNotifications(); + thread->clearNotifications(); i = _waiters.erase(i); continue; } @@ -578,12 +579,12 @@ void System::showNext() { i->second.when = k->second; break; } - history->skipNotification(); - current = history->currentNotification(); + thread->skipNotification(); + current = thread->currentNotification(); } while (current); } if (!current) { - _whenMaps.remove(history); + _whenMaps.remove(thread); i = _waiters.erase(i); continue; } @@ -591,7 +592,7 @@ void System::showNext() { if (!notify || next > when) { next = when; notify = current, - notifyHistory = history; + notifyThread = thread; } ++i; } @@ -606,11 +607,11 @@ void System::showNext() { break; } const auto notifyItem = notify->item; - const auto messageNotification = (notify->type - == ItemNotificationType::Message); - const auto isForwarded = messageNotification + const auto messageType = (notify->type + == Data::ItemNotificationType::Message); + const auto isForwarded = messageType && notifyItem->Has(); - const auto isAlbum = messageNotification + const auto isAlbum = messageType && notifyItem->groupId(); // Forwarded and album notify grouping. @@ -619,15 +620,15 @@ void System::showNext() { : nullptr; auto forwardedCount = isForwarded ? 1 : 0; - const auto history = notifyItem->history(); - const auto j = _whenMaps.find(history); + const auto thread = notifyItem->thread(); + const auto j = _whenMaps.find(thread); if (j == _whenMaps.cend()) { - history->clearNotifications(); + thread->clearNotifications(); } else { while (true) { - auto nextNotify = std::optional(); - history->skipNotification(); - if (!history->hasNotification()) { + auto nextNotify = std::optional(); + thread->skipNotification(); + if (!thread->hasNotification()) { break; } @@ -637,25 +638,26 @@ void System::showNext() { }); do { const auto k = j->second.find( - history->currentNotification()); + thread->currentNotification()); if (k != j->second.cend()) { - nextNotify = history->currentNotification(); - _waiters.emplace(notifyHistory, Waiter{ + nextNotify = thread->currentNotification(); + _waiters.emplace(notifyThread, Waiter{ .key = k->first, .when = k->second }); break; } - history->skipNotification(); - } while (history->hasNotification()); + thread->skipNotification(); + } while (thread->hasNotification()); if (!nextNotify || !groupedItem) { break; } const auto nextMessageNotification = (nextNotify->type - == ItemNotificationType::Message); + == Data::ItemNotificationType::Message); const auto canNextBeGrouped = nextMessageNotification - && ((isForwarded && nextNotify->item->Has()) + && ((isForwarded + && nextNotify->item->Has()) || (isAlbum && nextNotify->item->groupId())); const auto nextItem = canNextBeGrouped ? nextNotify->item.get() @@ -706,7 +708,7 @@ void System::showNext() { // to show the previous notification. showGrouped(); const auto reactionNotification - = (notify->type == ItemNotificationType::Reaction); + = (notify->type == Data::ItemNotificationType::Reaction); const auto reaction = reactionNotification ? notify->item->lookupUnreadReaction(notify->reactionSender) : Data::ReactionId(); @@ -720,9 +722,9 @@ void System::showNext() { } } - if (!history->hasNotification()) { - _waiters.remove(history); - _whenMaps.remove(history); + if (!thread->hasNotification()) { + _waiters.remove(thread); + _whenMaps.remove(thread); } } if (nextAlert) { @@ -785,7 +787,7 @@ void System::playSound(not_null session, DocumentId id) { Manager::DisplayOptions Manager::getNotificationOptions( HistoryItem *item, - ItemNotificationType type) const { + Data::ItemNotificationType type) const { const auto hideEverything = Core::App().passcodeLocked() || forceHideDetails(); const auto view = Core::App().settings().notifyView(); @@ -796,7 +798,7 @@ Manager::DisplayOptions Manager::getNotificationOptions( result.hideMessageText = hideEverything || (view > Core::Settings::NotifyView::ShowPreview); result.hideMarkAsRead = result.hideMessageText - || (type != ItemNotificationType::Message) + || (type != Data::ItemNotificationType::Message) || !item || ((item->out() || item->history()->peer->isSelf()) && item->isFromScheduled()); @@ -1054,8 +1056,8 @@ void NativeManager::doShowNotification(NotificationFields &&fields) { const auto options = getNotificationOptions( fields.item, (fields.reactionFrom - ? ItemNotificationType::Reaction - : ItemNotificationType::Message)); + ? Data::ItemNotificationType::Reaction + : Data::ItemNotificationType::Message)); const auto item = fields.item; const auto peer = item->history()->peer; const auto reactionFrom = fields.reactionFrom; diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index ec71281f0..ec353451a 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -12,13 +12,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" class History; -struct ItemNotification; -enum class ItemNotificationType; namespace Data { class Session; class CloudImageView; class ForumTopic; +class Thread; +struct ItemNotification; +enum class ItemNotificationType; } // namespace Data namespace Main { @@ -31,17 +32,15 @@ class Manager; } // namespace Notifications } // namespace Platform -namespace Media { -namespace Audio { +namespace Media::Audio { class Track; -} // namespace Audio -} // namespace Media +} // namespace Media::Audio namespace Window { - class SessionController; +} // namespace Window -namespace Notifications { +namespace Window::Notifications { enum class ManagerType { Dummy, @@ -62,19 +61,17 @@ enum class ChangeType { DemoIsHidden, }; -} // namespace Notifications -} // namespace Window +} // namespace Window::Notifications namespace base { template <> -struct custom_is_fast_copy_type : public std::true_type { +struct custom_is_fast_copy_type : std::true_type { }; } // namespace base -namespace Window { -namespace Notifications { +namespace Window::Notifications { class Manager; @@ -90,7 +87,7 @@ public: [[nodiscard]] ManagerType managerType() const; void checkDelayed(); - void schedule(ItemNotification notification); + void schedule(Data::ItemNotification notification); void clearFromTopic(not_null topic); void clearFromHistory(not_null history); void clearIncomingFromTopic(not_null topic); @@ -123,18 +120,17 @@ private: bool silent = false; }; struct NotificationInHistoryKey { - NotificationInHistoryKey(ItemNotification notification); - NotificationInHistoryKey(MsgId messageId, ItemNotificationType type); + NotificationInHistoryKey(Data::ItemNotification notification); + NotificationInHistoryKey( + MsgId messageId, + Data::ItemNotificationType type); MsgId messageId = 0; - ItemNotificationType type = ItemNotificationType(); + Data::ItemNotificationType type = Data::ItemNotificationType(); - friend inline bool operator<( - NotificationInHistoryKey a, - NotificationInHistoryKey b) { - return std::pair(a.messageId, a.type) - < std::pair(b.messageId, b.type); - } + friend inline auto operator<=>( + NotificationInHistoryKey a, + NotificationInHistoryKey b) = default; }; struct Timing { crl::time delay = 0; @@ -152,10 +148,12 @@ private: } }; + void clearForThreadIf(Fn)> predicate); + [[nodiscard]] SkipState skipNotification( - ItemNotification notification) const; + Data::ItemNotification notification) const; [[nodiscard]] SkipState computeSkipState( - ItemNotification notification) const; + Data::ItemNotification notification) const; [[nodiscard]] Timing countTiming( not_null history, crl::time minimalDelay) const; @@ -170,16 +168,16 @@ private: DocumentId id); base::flat_map< - not_null, + not_null, base::flat_map> _whenMaps; - base::flat_map, Waiter> _waiters; - base::flat_map, Waiter> _settingWaiters; + base::flat_map, Waiter> _waiters; + base::flat_map, Waiter> _settingWaiters; base::Timer _waitTimer; base::Timer _waitForAllGroupedTimer; base::flat_map< - not_null, + not_null, base::flat_map> _whenAlerts; mutable base::flat_map< @@ -270,7 +268,7 @@ public: }; [[nodiscard]] DisplayOptions getNotificationOptions( HistoryItem *item, - ItemNotificationType type) const; + Data::ItemNotificationType type) const; [[nodiscard]] static TextWithEntities ComposeReactionEmoji( not_null session, const Data::ReactionId &reaction); @@ -405,7 +403,6 @@ protected: }; -QString WrapFromScheduled(const QString &text); +[[nodiscard]] QString WrapFromScheduled(const QString &text); -} // namespace Notifications -} // namespace Window +} // namespace Window::Notifications diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 0d526f8f8..3017e473d 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -823,8 +823,8 @@ void Notification::updateNotifyDisplay() { const auto options = manager()->getNotificationOptions( _item, (_reaction.empty() - ? ItemNotificationType::Message - : ItemNotificationType::Reaction)); + ? Data::ItemNotificationType::Message + : Data::ItemNotificationType::Reaction)); _hideReplyButton = options.hideReplyButton; int32 w = width(), h = height(); @@ -1118,7 +1118,9 @@ bool Notification::unlinkSession(not_null session) { } void Notification::enterEventHook(QEnterEvent *e) { - if (!_history) return; + if (!_history) { + return; + } manager()->stopAllHiding(); if (!_replyArea && canReply()) { toggleActionButtons(true); @@ -1126,7 +1128,9 @@ void Notification::enterEventHook(QEnterEvent *e) { } void Notification::leaveEventHook(QEvent *e) { - if (!_history) return; + if (!_history) { + return; + } manager()->startAllHiding(); toggleActionButtons(false); }