diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index 1f314aae0..c5ded1bcc 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1453,7 +1453,7 @@ stickerKeyword#fcfeb29c document_id:long keyword:Vector = StickerKeyword forumTopic#4a0005d9 flags:# pinned:flags.2?true id:int date:int title:string top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int = ForumTopic; -messages.forumTopics#ed93d3e flags:# count:int topics:Vector messages:Vector chats:Vector users:Vector pts:int next_date:flags.0?int = messages.ForumTopics; +messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector messages:Vector chats:Vector users:Vector pts:int = messages.ForumTopics; ---functions--- @@ -1856,6 +1856,7 @@ channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates; channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates; channels.createForumTopic#22cf4868 flags:# no_webpage:flags.3?true channel:InputChannel title:string media:flags.0?InputMedia message:string random_id:long entities:flags.1?Vector send_as:flags.2?InputPeer = Updates; channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics; +channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector = messages.ForumTopics; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 6aa6210b6..d9cd17423 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -13,10 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat_participant_status.h" #include "data/data_peer_bot_commands.h" -namespace Data { -class Forum; -} // namespace Data - struct ChannelLocation { QString address; Data::LocationPoint point; diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index 225ace200..13bc0ecdc 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -69,23 +69,27 @@ void Forum::requestTopics() { const auto &list = data.vtopics().v; for (const auto &topic : list) { const auto rootId = MsgId(topic.data().vid().v); - if (const auto i = _topics.find(rootId); i != end(_topics)) { - i->second->applyTopic(topic); - } else { - const auto raw = _topics.emplace( + const auto i = _topics.find(rootId); + const auto creating = (i == end(_topics)); + const auto raw = creating + ? _topics.emplace( rootId, std::make_unique(forum, rootId) - ).first->second.get(); - raw->applyTopic(topic); + ).first->second.get() + : i->second.get(); + raw->applyTopic(topic); + if (creating) { raw->addToChatList(FilterId(), topicsList()); } + if (const auto last = raw->lastServerMessage()) { + _offsetDate = last->date(); + _offsetId = last->id; + } + _offsetTopicId = rootId; } if (list.isEmpty() || list.size() == data.vcount().v) { _allLoaded = true; } - if (const auto date = data.vnext_date()) { - _offsetDate = date->v; - } _requestId = 0; _chatsListChanges.fire({}); if (_allLoaded) { @@ -97,21 +101,35 @@ void Forum::requestTopics() { }).send(); } -void Forum::topicAdded(not_null root) { - const auto rootId = root->id; +void Forum::applyTopicAdded(MsgId rootId, const QString &title) { if (const auto i = _topics.find(rootId); i != end(_topics)) { - //i->second->applyTopic(topic); + i->second->applyTitle(title); } else { const auto raw = _topics.emplace( rootId, std::make_unique(_forum, rootId) ).first->second.get(); - //raw->applyTopic(topic); + raw->applyTitle(title); raw->addToChatList(FilterId(), topicsList()); _chatsListChanges.fire({}); } } +void Forum::applyTopicRemoved(MsgId rootId) { + //if (const auto i = _topics.find(rootId)) { + // _topics.erase(i); + //} +} + +ForumTopic *Forum::topicFor(not_null item) { + if (const auto rootId = item->replyToTop()) { + if (const auto i = _topics.find(rootId); i != end(_topics)) { + return i->second.get(); + } + } + return nullptr; +} + rpl::producer<> Forum::chatsListChanges() const { return _chatsListChanges.events(); } diff --git a/Telegram/SourceFiles/data/data_forum.h b/Telegram/SourceFiles/data/data_forum.h index 51fc36230..c64512135 100644 --- a/Telegram/SourceFiles/data/data_forum.h +++ b/Telegram/SourceFiles/data/data_forum.h @@ -29,7 +29,9 @@ public: [[nodiscard]] rpl::producer<> chatsListChanges() const; [[nodiscard]] rpl::producer<> chatsListLoadedEvents() const; - void topicAdded(not_null root); + void applyTopicAdded(MsgId rootId, const QString &title); + void applyTopicRemoved(MsgId rootId); + [[nodiscard]] ForumTopic *topicFor(not_null item); private: const not_null _forum; diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index 897066933..b588498c8 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -37,13 +37,7 @@ void ForumTopic::applyTopic(const MTPForumTopic &topic) { Expects(_rootId == topic.data().vid().v); const auto &data = topic.data(); - const auto title = qs(data.vtitle()); - if (_title != title) { - _title = title; - ++_titleVersion; - indexTitleParts(); - updateChatListEntry(); - } + applyTitle(qs(data.vtitle())); const auto pinned = _list->pinned(); if (data.is_pinned()) { @@ -216,7 +210,7 @@ TimeId ForumTopic::adjustedChatListTimeId() const { } int ForumTopic::fixedOnTopIndex() const { - return kArchiveFixOnTopIndex; + return 0; } bool ForumTopic::shouldBeInChatList() const { @@ -241,6 +235,38 @@ bool ForumTopic::lastServerMessageKnown() const { return _lastServerMessage.has_value(); } +void ForumTopic::applyTitle(const QString &title) { + if (_title == title) { + return; + } + _title = title; + ++_titleVersion; + indexTitleParts(); + updateChatListEntry(); +} + +void ForumTopic::applyItemAdded(not_null item) { + setLastMessage(item); +} + +void ForumTopic::applyItemRemoved(MsgId id) { + if (const auto lastItem = lastMessage()) { + if (lastItem->id == id) { + _lastMessage = std::nullopt; + } + } + if (const auto lastServerItem = lastServerMessage()) { + if (lastServerItem->id == id) { + _lastServerMessage = std::nullopt; + } + } + if (const auto chatListItem = _chatListMessage.value_or(nullptr)) { + if (chatListItem->id == id) { + _chatListMessage = std::nullopt; + } + } +} + int ForumTopic::unreadCount() const { return _unreadCount ? *_unreadCount : 0; } diff --git a/Telegram/SourceFiles/data/data_forum_topic.h b/Telegram/SourceFiles/data/data_forum_topic.h index 844dbd14e..9189e1957 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.h +++ b/Telegram/SourceFiles/data/data_forum_topic.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "dialogs/dialogs_entry.h" +#include "dialogs/ui/dialogs_message_view.h" class ChannelData; @@ -56,6 +57,10 @@ public: [[nodiscard]] bool lastMessageKnown() const; [[nodiscard]] bool lastServerMessageKnown() const; + void applyTitle(const QString &title); + void applyItemAdded(not_null item); + void applyItemRemoved(MsgId id); + void loadUserpic() override; void paintUserpic( Painter &p, @@ -73,6 +78,9 @@ public: void setUnreadMark(bool unread); [[nodiscard]] bool unreadMark() const; + Ui::Text::String cloudDraftTextCache; + Dialogs::Ui::MessageView lastItemDialogsView; + private: void indexTitleParts(); void applyTopicTopMessage(MsgId topMessageId); diff --git a/Telegram/SourceFiles/data/data_groups.cpp b/Telegram/SourceFiles/data/data_groups.cpp index 5c0756cac..64e9f3afa 100644 --- a/Telegram/SourceFiles/data/data_groups.cpp +++ b/Telegram/SourceFiles/data/data_groups.cpp @@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/ui/dialogs_message_view.h" #include "data/data_media_types.h" #include "data/data_session.h" +#include "data/data_forum.h" +#include "data/data_forum_topic.h" namespace Data { namespace { @@ -145,6 +147,11 @@ void Groups::refreshViews(const HistoryItemsList &items) { for (const auto &item : items) { _data->requestItemViewRefresh(item); history->lastItemDialogsView.itemInvalidated(item); + if (const auto forum = history->peer->forum()) { + if (const auto topic = forum->topicFor(item)) { + topic->lastItemDialogsView.itemInvalidated(item); + } + } } } diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 75c302df3..549f6beaf 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -882,6 +882,13 @@ bool PeerData::isRepliesChat() const { : kTestId) == id; } +Data::Forum *PeerData::forum() const { + if (const auto channel = asChannel()) { + return channel->forum(); + } + return nullptr; +} + bool PeerData::canWrite() const { if (const auto user = asUser()) { return user->canWrite(); diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 9c880c09b..e6a8cdeaa 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -32,6 +32,7 @@ class Session; namespace Data { +class Forum; class Session; class GroupCall; class CloudImageView; @@ -195,6 +196,8 @@ public: return isUser() && !(id.value % 1000); } + [[nodiscard]] Data::Forum *forum() const; + [[nodiscard]] std::optional notifyMuteUntil() const { return _notify.muteUntil(); } diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 4651f4b8b..9cd994050 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -67,6 +67,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_histories.h" #include "data/data_peer_values.h" #include "data/data_premium_limits.h" +#include "data/data_forum.h" +#include "data/data_forum_topic.h" #include "data/stickers/data_custom_emoji.h" #include "base/platform/base_platform_info.h" #include "base/unixtime.h" @@ -3793,7 +3795,9 @@ void Session::refreshChatListEntry(Dialogs::Key key) { const auto entry = key.entry(); const auto history = key.history(); - const auto mainList = chatsList(entry->folder()); + const auto mainList = entry->asTopic() + ? entry->asTopic()->forum()->peer->forum()->topicsList() + : chatsList(entry->folder()); auto event = ChatListEntryRefresh{ .key = key }; const auto creating = event.existenceChanged = !entry->inChatList(); if (event.existenceChanged) { @@ -3848,6 +3852,7 @@ void Session::removeChatListEntry(Dialogs::Key key) { return; } Assert(entry->folderKnown()); + for (const auto &filter : _chatsFilters->list()) { const auto id = filter.id(); if (id && entry->inChatList(id)) { @@ -3859,7 +3864,9 @@ void Session::removeChatListEntry(Dialogs::Key key) { }); } } - const auto mainList = chatsList(entry->folder()); + const auto mainList = entry->asTopic() + ? entry->asTopic()->forum()->peer->forum()->topicsList() + : chatsList(entry->folder()); entry->removeFromChatList(0, mainList); _chatListEntryRefreshes.fire(ChatListEntryRefresh{ .key = key, diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index cea7af9ec..e69810707 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -67,7 +67,7 @@ Data::Folder *Entry::asFolder() { : nullptr; } -Data::ForumTopic *Entry::asForumTopic() { +Data::ForumTopic *Entry::asTopic() { return (_type == Type::ForumTopic) ? static_cast(this) : nullptr; @@ -186,9 +186,6 @@ const Ui::Text::String &Entry::chatListNameText() const { } void Entry::setChatListExistence(bool exists) { - if (asForumTopic()) { - return; - } if (exists && _sortKeyInChatList) { owner().refreshChatListEntry(this); updateChatListEntry(); @@ -263,7 +260,7 @@ not_null Entry::addToChatList( void Entry::removeFromChatList( FilterId filterId, not_null list) { - if (!asForumTopic() && isPinnedDialog(filterId)) { + if (!asTopic() && isPinnedDialog(filterId)) { owner().setChatPinned(this, filterId, false); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.h b/Telegram/SourceFiles/dialogs/dialogs_entry.h index d18c5a238..24d355742 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.h +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.h @@ -107,7 +107,7 @@ public: History *asHistory(); Data::Folder *asFolder(); - Data::ForumTopic *asForumTopic(); + Data::ForumTopic *asTopic(); PositionChange adjustByPosInChatList( FilterId filterId, diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.cpp b/Telegram/SourceFiles/dialogs/dialogs_key.cpp index 0d6c570b0..1972a78bd 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_key.cpp @@ -25,7 +25,7 @@ Key::Key(History *history) : _value(history) { Key::Key(Data::Folder *folder) : _value(folder) { } -Key::Key(Data::ForumTopic *forumTopic) : _value(forumTopic) { +Key::Key(Data::ForumTopic *topic) : _value(topic) { } Key::Key(not_null history) : _value(history) { @@ -34,7 +34,7 @@ Key::Key(not_null history) : _value(history) { Key::Key(not_null folder) : _value(folder) { } -Key::Key(not_null forumTopic) : _value(forumTopic) { +Key::Key(not_null topic) : _value(topic) { } not_null Key::entry() const { @@ -51,8 +51,8 @@ Folder *Key::folder() const { return _value ? _value->asFolder() : nullptr; } -ForumTopic *Key::forumTopic() const { - return _value ? _value->asForumTopic() : nullptr; +ForumTopic *Key::topic() const { + return _value ? _value->asTopic() : nullptr; } PeerData *Key::peer() const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_key.h b/Telegram/SourceFiles/dialogs/dialogs_key.h index f77acc3bf..c46ec448e 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_key.h +++ b/Telegram/SourceFiles/dialogs/dialogs_key.h @@ -28,12 +28,12 @@ public: } Key(History *history); Key(Data::Folder *folder); - Key(Data::ForumTopic *forumTopic); + Key(Data::ForumTopic *topic); Key(not_null entry) : _value(entry) { } Key(not_null history); Key(not_null folder); - Key(not_null forumTopic); + Key(not_null topic); explicit operator bool() const { return (_value != nullptr); @@ -41,7 +41,7 @@ public: not_null entry() const; History *history() const; Data::Folder *folder() const; - Data::ForumTopic *forumTopic() const; + Data::ForumTopic *topic() const; PeerData *peer() const; inline bool operator<(const Key &other) const { diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index c572dcc12..60d36ecd1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -98,6 +98,9 @@ public: [[nodiscard]] Data::Folder *folder() const { return _id.folder(); } + [[nodiscard]] Data::ForumTopic *topic() const { + return _id.topic(); + } [[nodiscard]] not_null entry() const { return _id.entry(); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index cb53901c3..fc2d07692 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -263,10 +263,10 @@ Widget::Widget( const auto openSearchResult = !controller->selectingPeer() && row.filteredRow; const auto history = row.key.history(); - if (const auto forumTopic = row.key.forumTopic()) { + if (const auto topic = row.key.topic()) { controller->showRepliesForMessage( - forumTopic->forum(), - forumTopic->rootId()); + topic->forum(), + topic->rootId()); } else if (history && history->peer->isForum()) { controller->openForum(history->peer->asChannel()); } else if (history) { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index 8fd3484fe..48f2c3541 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -35,6 +35,7 @@ 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 { @@ -852,6 +853,7 @@ void RowPainter::paint( bool paused) { const auto entry = row->entry(); const auto history = row->history(); + const auto topic = row->topic(); const auto peer = history ? history->peer.get() : nullptr; const auto unreadCount = entry->chatListUnreadCount(); const auto unreadMark = entry->chatListUnreadMark(); @@ -960,22 +962,23 @@ void RowPainter::paint( color, ms) : false; + const auto view = actionWasPainted + ? nullptr + : history + ? &history->lastItemDialogsView + : topic + ? &topic->lastItemDialogsView + : nullptr; if (const auto folder = row->folder()) { PaintListEntryText(p, rect, active, selected, row, ms, paused); - } else if (history && !actionWasPainted) { - if (!history->lastItemDialogsView.prepared(item)) { - history->lastItemDialogsView.prepare( + } else if (view) { + if (!view->prepared(item)) { + view->prepare( item, - [=] { history->updateChatListEntry(); }, + [=] { entry->updateChatListEntry(); }, {}); } - history->lastItemDialogsView.paint( - p, - rect, - active, - selected, - ms, - paused); + view->paint(p, rect, active, selected, ms, paused); } }; const auto paintCounterCallback = [&] { diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index a9fda3af7..7e3ffa121 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_send_action.h" #include "data/data_folder.h" #include "data/data_forum.h" +#include "data/data_forum_topic.h" #include "data/data_photo.h" #include "data/data_channel.h" #include "data/data_chat.h" @@ -159,6 +160,15 @@ void History::itemRemoved(not_null item) { if (IsClientMsgId(item->id)) { unregisterClientSideMessage(item); } + if (const auto forum = peer->forum()) { + if (const auto topic = forum->topicFor(item)) { + if (topic->rootId() == item->id) { + forum->applyTopicRemoved(item->id); + } else { + topic->applyItemRemoved(item->id); + } + } + } if (const auto chat = peer->asChat()) { if (const auto to = chat->getMigrateToChannel()) { if (const auto history = owner().historyLoaded(to)) { @@ -1124,9 +1134,13 @@ void History::newItemAdded(not_null item) { if (!folderKnown()) { owner().histories().requestDialogEntry(this); } - if (item->isTopicStart() && peer->isForum()) { + if (peer->isForum()) { if (const auto forum = peer->asChannel()->forum()) { - forum->topicAdded(item); + /*if (item->isTopicStart()) { // #TODO forum isTopicStart legacy? + forum->topicAdded(item); + } else */if (const auto topic = forum->topicFor(item)) { + topic->applyItemAdded(item); + } } } } diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 0a973f7b8..cc4442d81 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_service_message.h" #include "history/view/history_view_item_preview.h" #include "data/data_folder.h" +#include "data/data_forum.h" #include "data/data_session.h" #include "data/data_media_types.h" #include "data/data_game.h" @@ -771,6 +772,10 @@ void HistoryService::applyAction(const MTPMessageAction &action) { this, _from, data.vmonths().v); + }, [&](const MTPDmessageActionTopicCreate &data) { + if (const auto forum = history()->peer->forum()) { + forum->applyTopicAdded(id, qs(data.vtitle())); + } }, [](const auto &) { }); }