diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 5e5e23e1b..97ea31140 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -66,7 +66,9 @@ Data::ChatBotCommands::Changed MegagroupInfo::setBotCommands( void MegagroupInfo::ensureForum(not_null that) { if (!_forum) { - _forum = std::make_unique(that->owner().history(that)); + const auto history = that->owner().history(that); + _forum = std::make_unique(history); + history->forumChanged(nullptr); } } @@ -75,7 +77,12 @@ Data::Forum *MegagroupInfo::forum() const { } std::unique_ptr MegagroupInfo::takeForumData() { - return std::move(_forum); + if (auto result = base::take(_forum)) { + result->history()->forumChanged(result.get()); + return result; + } + return nullptr; + } ChannelData::ChannelData(not_null owner, PeerId id) diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp index 6a433db9a..06a23030b 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.cpp +++ b/Telegram/SourceFiles/data/data_chat_filters.cpp @@ -212,7 +212,7 @@ bool ChatFilter::contains(not_null history) const { && history->folderKnown() && !history->folder())) && (!(_flags & Flag::NoRead) - || history->unreadCount() + || history->chatListUnreadCount() || history->unreadMark() || history->unreadMentions().has() || history->fakeUnreadWhileOpened()) diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index 6c2c7a8eb..cf3db1cbc 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -71,17 +71,6 @@ Folder::Folder(not_null owner, FolderId id) }, _lifetime); } -void Folder::updateChatListEntryPostponed() { - if (_updateChatListEntryPostponed) { - return; - } - _updateChatListEntryPostponed = true; - Ui::PostponeCall(this, [=] { - updateChatListEntry(); - _updateChatListEntryPostponed = false; - }); -} - FolderId Folder::id() const { return _id; } diff --git a/Telegram/SourceFiles/data/data_folder.h b/Telegram/SourceFiles/data/data_folder.h index dd18c6d67..986fa9368 100644 --- a/Telegram/SourceFiles/data/data_folder.h +++ b/Telegram/SourceFiles/data/data_folder.h @@ -81,7 +81,6 @@ private: int chatListNameVersion() const override; void reorderLastHistories(); - void updateChatListEntryPostponed(); void paintUserpic( Painter &p, @@ -101,7 +100,6 @@ private: std::vector> _lastHistories; HistoryItem *_chatListMessage = nullptr; uint32 _chatListViewVersion = 0; - bool _updateChatListEntryPostponed = false; //rpl::variable _unreadPosition; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index ef67bd42b..44675f674 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -30,6 +30,7 @@ namespace Data { namespace { constexpr auto kTopicsFirstLoad = 20; +constexpr auto kLoadedTopicsMinCount = 20; constexpr auto kTopicsPerPage = 500; constexpr auto kGeneralColorId = 0xA9A9A9; @@ -39,6 +40,10 @@ Forum::Forum(not_null history) : _history(history) , _topicsList(&session(), FilterId(0), rpl::single(1)) { Expects(_history->peer->isChannel()); + + if (_history->inChatList()) { + preloadTopics(); + } } Forum::~Forum() { @@ -89,6 +94,12 @@ rpl::producer> Forum::topicDestroyed() const { return _topicDestroyed.events(); } +void Forum::preloadTopics() { + if (topicsList()->indexed()->size() < kLoadedTopicsMinCount) { + requestTopics(); + } +} + void Forum::requestTopics() { if (_allLoaded || _requestId) { return; diff --git a/Telegram/SourceFiles/data/data_forum.h b/Telegram/SourceFiles/data/data_forum.h index 85062043f..b61faaa32 100644 --- a/Telegram/SourceFiles/data/data_forum.h +++ b/Telegram/SourceFiles/data/data_forum.h @@ -38,6 +38,7 @@ public: [[nodiscard]] auto topicDestroyed() const -> rpl::producer>; + void preloadTopics(); void requestTopics(); [[nodiscard]] rpl::producer<> chatsListChanges() const; [[nodiscard]] rpl::producer<> chatsListLoadedEvents() const; @@ -68,6 +69,10 @@ public: void clearAllUnreadReactions(); void enumerateTopics(Fn)> action) const; + [[nodiscard]] rpl::lifetime &lifetime() { + return _lifetime; + } + private: struct TopicRequest { mtpRequestId id = 0; @@ -97,6 +102,8 @@ private: rpl::event_stream<> _chatsListChanges; rpl::event_stream<> _chatsListLoadedEvents; + rpl::lifetime _lifetime; + }; } // namespace Data diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index 31e179a35..73f41b451 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -652,11 +652,7 @@ not_null ForumTopic::sendActionPainter() { } int ForumTopic::chatListUnreadCount() const { - const auto state = chatListUnreadState(); - return state.marks - + (Core::App().settings().countUnreadMessages() - ? state.messages - : state.chats); + return unreadCount(); } Dialogs::UnreadState ForumTopic::chatListUnreadState() const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index 9f3bc1a4d..4155d3f9c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -46,7 +46,11 @@ uint64 PinnedDialogPos(int pinnedIndex) { Entry::Entry(not_null owner, Type type) : _owner(owner) -, _type(type) { +, _flags((type == Type::History) + ? (Flag::IsThread | Flag::IsHistory) + : (type == Type::ForumTopic) + ? Flag::IsThread + : Flag(0)) { } Entry::~Entry() = default; @@ -60,23 +64,23 @@ Main::Session &Entry::session() const { } History *Entry::asHistory() { - return (_type == Type::History) ? static_cast(this) : nullptr; + return (_flags & Flag::IsHistory) ? static_cast(this) : nullptr; } Data::Folder *Entry::asFolder() { - return (_type == Type::Folder) - ? static_cast(this) - : nullptr; + return (_flags & Flag::IsThread) + ? nullptr + : static_cast(this); } Data::Thread *Entry::asThread() { - return (_type == Type::History || _type == Type::ForumTopic) + return (_flags & Flag::IsThread) ? static_cast(this) : nullptr; } Data::ForumTopic *Entry::asTopic() { - return (_type == Type::ForumTopic) + return ((_flags & Flag::IsThread) && !(_flags & Flag::IsHistory)) ? static_cast(this) : nullptr; } @@ -298,7 +302,20 @@ void Entry::addChatListEntryByLetter( } void Entry::updateChatListEntry() { + _flags &= ~Flag::UpdatePostponed; session().changes().entryUpdated(this, Data::EntryUpdate::Flag::Repaint); } +void Entry::updateChatListEntryPostponed() { + if (_flags & Flag::UpdatePostponed) { + return; + } + _flags |= Flag::UpdatePostponed; + Ui::PostponeCall(this, [=] { + if (_flags & Flag::UpdatePostponed) { + updateChatListEntry(); + } + }); +} + } // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index 5f7d49e40..8541b9037 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/flat_map.h" #include "base/weak_ptr.h" +#include "base/flags.h" #include "dialogs/dialogs_key.h" #include "ui/unread_badge.h" @@ -145,6 +146,7 @@ public: QChar letter, not_null row); void updateChatListEntry(); + void updateChatListEntryPostponed(); [[nodiscard]] bool isPinnedDialog(FilterId filterId) const { return lookupPinnedIndex(filterId) != 0; } @@ -214,6 +216,14 @@ protected: [[nodiscard]] int lookupPinnedIndex(FilterId filterId) const; private: + enum class Flag : uchar { + IsThread = (1 << 0), + IsHistory = (1 << 1), + UpdatePostponed = (1 << 2), + }; + friend inline constexpr bool is_flag_type(Flag) { return true; } + using Flags = base::flags; + virtual void changedChatListPinHook(); void pinnedIndexChanged(FilterId filterId, int was, int now); [[nodiscard]] uint64 computeSortPosition(FilterId filterId) const; @@ -233,7 +243,7 @@ private: mutable Ui::Text::String _chatListNameText; mutable int _chatListNameVersion = 0; TimeId _timeId = 0; - const Type _type; + Flags _flags; }; diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index ea75cd16d..43ee090ed 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -47,7 +47,7 @@ namespace { const auto name = history->peer->name(); return TextWithEntities{ .text = name, - .entities = (history->unreadCount() > 0) + .entities = (history->chatListUnreadCount() > 0) ? EntitiesInText{ { EntityType::Semibold, 0, int(name.size()), QString() }, { EntityType::PlainLink, 0, int(name.size()), QString() }, diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 2507f9989..aa7ea788e 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -2067,6 +2067,13 @@ History *History::migrateSibling() const { } int History::chatListUnreadCount() const { + if (peer->isForum()) { + const auto state = chatListUnreadState(); + return state.marks + + (Core::App().settings().countUnreadMessages() + ? state.messages + : state.chats); + } const auto result = unreadCount(); if (const auto migrated = migrateSibling()) { return result + migrated->unreadCount(); @@ -2084,10 +2091,24 @@ bool History::chatListUnreadMark() const { } bool History::chatListMutedBadge() const { + if (const auto forum = peer->forum()) { + const auto state = forum->topicsList()->unreadState(); + return (state.marksMuted >= state.marks) + && (Core::App().settings().countUnreadMessages() + ? (state.messagesMuted >= state.messages) + : (state.chatsMuted >= state.chats)); + } return muted(); } Dialogs::UnreadState History::chatListUnreadState() const { + if (const auto forum = peer->forum()) { + return forum->topicsList()->unreadState(); + } + return computeUnreadState(); +} + +Dialogs::UnreadState History::computeUnreadState() const { auto result = Dialogs::UnreadState(); const auto count = _unreadCount.value_or(0); const auto mark = !count && unreadMark(); @@ -2639,6 +2660,10 @@ void History::applyDialog( draft->c_draftMessage()); } owner().histories().dialogEntryApplied(this); + + if (const auto forum = inChatList() ? peer->forum() : nullptr) { + forum->preloadTopics(); + } } void History::dialogEntryApplied() { @@ -2868,6 +2893,28 @@ const Data::Thread *History::threadFor(MsgId topicRootId) const { return const_cast(this)->threadFor(topicRootId); } +void History::forumChanged(Data::Forum *old) { + if (inChatList()) { + notifyUnreadStateChange(old + ? old->topicsList()->unreadState() + : computeUnreadState()); + } + + if (const auto forum = peer->forum()) { + _flags |= Flag::IsForum; + + forum->topicsList()->unreadStateChanges( + ) | rpl::filter([=] { + return (_flags & Flag::IsForum) && inChatList(); + }) | rpl::start_with_next([=](const Dialogs::UnreadState &old) { + notifyUnreadStateChange(old); + updateChatListEntryPostponed(); + }, forum->lifetime()); + } else { + _flags &= ~Flag::IsForum; + } +} + not_null History::migrateToOrMe() const { if (const auto to = peer->migrateTo()) { return owner().history(to); diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index 121d604fc..056cc3d8b 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -88,6 +88,8 @@ public: return _delegateMixin.get(); } + void forumChanged(Data::Forum *old); + not_null migrateToOrMe() const; History *migrateFrom() const; MsgRange rangeForDifferenceRequest() const; @@ -461,6 +463,7 @@ private: enum class Flag : uchar { HasPendingResizedItems = (1 << 0), IsTopPromoted = (1 << 1), + IsForum = (1 << 2), }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { @@ -571,6 +574,7 @@ private: HistoryService *insertJoinedMessage(); void insertMessageToBlocks(not_null item); + [[nodiscard]] Dialogs::UnreadState computeUnreadState() const; void setFolderPointer(Data::Folder *folder); int chatListNameVersion() const override;