From 358e64f2ccb0e54336de5b84c3ae1e23bb0d1734 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 16 May 2025 16:29:40 +0400 Subject: [PATCH] Show monoforums as forums in chats list. --- Telegram/SourceFiles/data/data_channel.cpp | 2 +- Telegram/SourceFiles/data/data_forum.cpp | 2 +- Telegram/SourceFiles/data/data_histories.cpp | 22 ++-- .../SourceFiles/data/data_saved_messages.cpp | 75 ++++++++++++- .../SourceFiles/data/data_saved_messages.h | 12 +++ .../SourceFiles/data/data_saved_sublist.cpp | 11 ++ .../SourceFiles/dialogs/dialogs_entry.cpp | 7 ++ .../dialogs/dialogs_inner_widget.cpp | 9 +- Telegram/SourceFiles/dialogs/dialogs_row.cpp | 4 +- .../SourceFiles/dialogs/ui/dialogs_layout.cpp | 25 +++-- .../dialogs/ui/dialogs_message_view.cpp | 27 +++-- .../dialogs/ui/dialogs_message_view.h | 5 +- .../dialogs/ui/dialogs_topics_view.cpp | 102 ++++++++++++++++-- .../dialogs/ui/dialogs_topics_view.h | 15 ++- Telegram/SourceFiles/history/history.cpp | 10 +- Telegram/SourceFiles/history/history.h | 4 +- Telegram/SourceFiles/history/history_item.cpp | 24 +++-- Telegram/SourceFiles/history/history_item.h | 2 +- .../view/history_view_chat_section.cpp | 4 +- .../history/view/history_view_element.cpp | 2 +- .../view/history_view_top_bar_widget.cpp | 5 +- .../info/profile/info_profile_actions.cpp | 9 +- 22 files changed, 303 insertions(+), 75 deletions(-) diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 213737dd45..9250df02a9 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -247,7 +247,7 @@ void ChannelData::setFlags(ChannelDataFlags which) { if (const auto forum = this->forum()) { forum->preloadTopics(); } else if (const auto monoforum = this->monoforum()) { - monoforum->loadMore(); + monoforum->preloadSublists(); } } } diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp index 4172ad1806..80f51c42b2 100644 --- a/Telegram/SourceFiles/data/data_forum.cpp +++ b/Telegram/SourceFiles/data/data_forum.cpp @@ -202,7 +202,7 @@ void Forum::applyTopicDeleted(MsgId rootId) { } void Forum::reorderLastTopics() { - // We want first kShowChatNamesCount histories, by last message date. + // We want first kShowTopicNamesCount histories, by last message date. const auto pred = [](not_null a, not_null b) { const auto aItem = a->chatListMessage(); const auto bItem = b->chatListMessage(); diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index bd7093579c..f13dccc97a 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -60,14 +60,14 @@ MTPInputReplyTo ReplyToForMTP( && (to->history() != history || to->id != replyingToTopicId)) ? to->topicRootId() : replyingToTopicId; - const auto possibleMonoforumPeer = (to && to->savedSublistPeer()) - ? to->savedSublistPeer() + const auto possibleMonoforumPeerId = (to && to->sublistPeerId()) + ? to->sublistPeerId() : replyTo.monoforumPeerId - ? history->owner().peer(replyTo.monoforumPeerId).get() - : history->session().user().get(); - const auto replyToMonoforumPeer = history->peer->amMonoforumAdmin() - ? possibleMonoforumPeer - : nullptr; + ? replyTo.monoforumPeerId + : history->session().user()->id; + const auto replyToMonoforumPeerId = history->peer->amMonoforumAdmin() + ? possibleMonoforumPeerId + : PeerId(); const auto external = replyTo.messageId && (replyTo.messageId.peer != history->peer->id || replyingToTopicId != replyToTopicId); @@ -82,7 +82,9 @@ MTPInputReplyTo ReplyToForMTP( | (replyTo.quote.text.isEmpty() ? Flag() : (Flag::f_quote_text | Flag::f_quote_offset)) - | (replyToMonoforumPeer ? Flag::f_monoforum_peer_id : Flag()) + | (replyToMonoforumPeerId + ? Flag::f_monoforum_peer_id + : Flag()) | (quoteEntities.v.isEmpty() ? Flag() : Flag::f_quote_entities)), @@ -94,8 +96,8 @@ MTPInputReplyTo ReplyToForMTP( MTP_string(replyTo.quote.text), quoteEntities, MTP_int(replyTo.quoteOffset), - (replyToMonoforumPeer - ? replyToMonoforumPeer->input + (replyToMonoforumPeerId + ? history->owner().peer(replyToMonoforumPeerId)->input : MTPInputPeer())); } else if (history->peer->amMonoforumAdmin() && replyTo.monoforumPeerId) { diff --git a/Telegram/SourceFiles/data/data_saved_messages.cpp b/Telegram/SourceFiles/data/data_saved_messages.cpp index 39b5821a08..82fc03c3a5 100644 --- a/Telegram/SourceFiles/data/data_saved_messages.cpp +++ b/Telegram/SourceFiles/data/data_saved_messages.cpp @@ -25,6 +25,7 @@ constexpr auto kFirstPerPage = 10; constexpr auto kListPerPage = 100; constexpr auto kListFirstPerPage = 20; constexpr auto kLoadedSublistsMinCount = 20; +constexpr auto kShowSublistNamesCount = 5; } // namespace @@ -33,13 +34,13 @@ SavedMessages::SavedMessages( ChannelData *parentChat) : _owner(owner) , _parentChat(parentChat) +, _parentHistory(parentChat ? owner->history(parentChat).get() : nullptr) , _chatsList( &_owner->session(), FilterId(), _owner->maxPinnedChatsLimitValue(this)) , _loadMore([=] { sendLoadMoreRequests(); }) { - if (_parentChat - && _parentChat->owner().history(_parentChat)->inChatList()) { + if (_parentHistory && _parentHistory->inChatList()) { preloadSublists(); } } @@ -128,6 +129,7 @@ void SavedMessages::sendLoadMore() { if (_chatsList.loaded()) { _chatsListLoadedEvents.fire({}); } + reorderLastSublists(); }).fail([=](const MTP::Error &error) { if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) { _unsupported = true; @@ -366,6 +368,75 @@ void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) { }); } +void SavedMessages::reorderLastSublists() { + if (!_parentHistory) { + return; + } + + // We want first kShowChatNamesCount histories, by last message date. + const auto pred = []( + not_null a, + not_null b) { + const auto aItem = a->chatListMessage(); + const auto bItem = b->chatListMessage(); + const auto aDate = aItem ? aItem->date() : TimeId(0); + const auto bDate = bItem ? bItem->date() : TimeId(0); + return aDate > bDate; + }; + _lastSublists.clear(); + _lastSublists.reserve(kShowSublistNamesCount + 1); + auto &&sublists = ranges::views::all( + *_chatsList.indexed() + ) | ranges::views::transform([](not_null row) { + return row->sublist(); + }); + auto nonPinnedChecked = 0; + for (const auto sublist : sublists) { + const auto i = ranges::upper_bound( + _lastSublists, + not_null(sublist), + pred); + if (size(_lastSublists) < kShowSublistNamesCount + || i != end(_lastSublists)) { + _lastSublists.insert(i, sublist); + } + if (size(_lastSublists) > kShowSublistNamesCount) { + _lastSublists.pop_back(); + } + if (!sublist->isPinnedDialog(FilterId()) + && ++nonPinnedChecked >= kShowSublistNamesCount) { + break; + } + } + ++_lastSublistsVersion; + _parentHistory->updateChatListEntry(); +} + +void SavedMessages::listMessageChanged(HistoryItem *from, HistoryItem *to) { + if (from || to) { + reorderLastSublists(); + } +} + +int SavedMessages::recentSublistsListVersion() const { + return _lastSublistsVersion; +} + +void SavedMessages::recentSublistsInvalidate( + not_null sublist) { + Expects(_parentHistory != nullptr); + + if (ranges::contains(_lastSublists, sublist)) { + ++_lastSublistsVersion; + _parentHistory->updateChatListEntry(); + } +} + +auto SavedMessages::recentSublists() const +-> const std::vector> & { + return _lastSublists; +} + rpl::producer<> SavedMessages::destroyed() const { if (!_parentChat) { return rpl::never<>(); diff --git a/Telegram/SourceFiles/data/data_saved_messages.h b/Telegram/SourceFiles/data/data_saved_messages.h index 983fb7d08e..dd03e2d2a8 100644 --- a/Telegram/SourceFiles/data/data_saved_messages.h +++ b/Telegram/SourceFiles/data/data_saved_messages.h @@ -49,18 +49,27 @@ public: void apply(const MTPDupdatePinnedSavedDialogs &update); void apply(const MTPDupdateSavedDialogPinned &update); + void listMessageChanged(HistoryItem *from, HistoryItem *to); + [[nodiscard]] int recentSublistsListVersion() const; + void recentSublistsInvalidate(not_null sublist); + [[nodiscard]] auto recentSublists() const + -> const std::vector> &; + [[nodiscard]] rpl::lifetime &lifetime(); private: void loadPinned(); void apply(const MTPmessages_SavedDialogs &result, bool pinned); + void reorderLastSublists(); + void sendLoadMore(); void sendLoadMore(not_null sublist); void sendLoadMoreRequests(); const not_null _owner; ChannelData *_parentChat = nullptr; + History *_parentHistory = nullptr; rpl::event_stream> _sublistDestroyed; @@ -81,6 +90,9 @@ private: base::flat_set> _loadMoreSublistsScheduled; bool _loadMoreScheduled = false; + std::vector> _lastSublists; + int _lastSublistsVersion = 0; + rpl::event_stream<> _chatsListChanges; rpl::event_stream<> _chatsListLoadedEvents; diff --git a/Telegram/SourceFiles/data/data_saved_sublist.cpp b/Telegram/SourceFiles/data/data_saved_sublist.cpp index c33361008c..367570cc0e 100644 --- a/Telegram/SourceFiles/data/data_saved_sublist.cpp +++ b/Telegram/SourceFiles/data/data_saved_sublist.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_saved_sublist.h" #include "data/data_histories.h" +#include "data/data_channel.h" #include "data/data_peer.h" #include "data/data_user.h" #include "data/data_saved_messages.h" @@ -80,6 +81,7 @@ void SavedSublist::applyMaybeLast(not_null item, bool added) { : (IsServerMsgId(b->id) ? false : (a->id < b->id)); }; + const auto was = _items.empty() ? nullptr : _items.front().get(); if (_items.empty()) { _items.push_back(item); } else if (_items.front() == item) { @@ -104,6 +106,8 @@ void SavedSublist::applyMaybeLast(not_null item, bool added) { if (_items.front() == item) { setChatListTimeId(item->date()); resolveChatListMessageGroup(); + + _parent->listMessageChanged(was, item.get()); } _changed.fire({}); } @@ -132,6 +136,8 @@ void SavedSublist::removeOne(not_null item) { } else { setChatListTimeId(_items.front()->date()); } + + _parent->listMessageChanged(item.get(), chatListMessage()); } if (removed || _fullCount) { _changed.fire({}); @@ -195,6 +201,11 @@ int SavedSublist::fixedOnTopIndex() const { } bool SavedSublist::shouldBeInChatList() const { + if (const auto monoforum = _parent->parentChat()) { + if (monoforum == sublistPeer()) { + return false; + } + } return isPinnedDialog(FilterId()) || !_items.empty(); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp index 899fdde8db..747cb519af 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_entry.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_entry.cpp @@ -229,6 +229,13 @@ uint64 Entry::computeSortPosition(FilterId filterId) const { } void Entry::updateChatListExistence() { + if (const auto history = asHistory()) { + if (const auto channel = history->peer->asMonoforum()) { + if (!folderKnown()) { + history->clearFolder(); + } + } + } setChatListExistence(shouldBeInChatList()); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 2df6141026..deab114692 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -892,10 +892,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) { const auto active = mayBeActive && isRowActive(row, activeEntry); const auto history = key.history(); const auto forum = history && history->isForum(); - if (forum && !_topicJumpCache) { + const auto monoforum = history && history->amMonoforumAdmin(); + if ((forum || monoforum) && !_topicJumpCache) { _topicJumpCache = std::make_unique(); } - const auto expanding = forum + const auto expanding = (forum || monoforum) && (history->peer->id == childListShown.peerId); context.rightButton = maybeCacheRightButton(row); if (history) { @@ -921,14 +922,14 @@ void InnerWidget::paintEvent(QPaintEvent *e) { } } - context.st = (forum ? &st::forumDialogRow : _st.get()); + context.st = (forum || monoforum) ? &st::forumDialogRow : _st.get(); auto chatsFilterTags = std::vector(); if (context.narrow) { context.chatsFilterTags = nullptr; } else if (row->entry()->hasChatsFilterTags(context.filter)) { const auto a = active; - context.st = forum + context.st = (forum || monoforum) ? &st::taggedForumDialogRow : &st::taggedDialogRow; auto availableWidth = context.width diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index c39133e52a..bac34785a1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -320,7 +320,7 @@ Row::~Row() { void Row::recountHeight(float64 narrowRatio, FilterId filterId) { if (const auto history = _id.history()) { const auto hasTags = _id.entry()->hasChatsFilterTags(filterId); - _height = history->isForum() + _height = (history->isForum() || history->amMonoforumAdmin()) ? anim::interpolate( hasTags ? st::taggedForumDialogRow.height @@ -466,7 +466,7 @@ void Row::PaintCornerBadgeFrame( for (auto i = 0; i != storiesUnreadCount; ++i) { segments.push_back({ storiesUnreadBrush, storiesUnread }); } - if (peer && peer->forum()) { + if (peer && (peer->forum() || peer->monoforum())) { const auto radius = context.st->photoSize * Ui::ForumUserpicRadiusMultiplier(); Ui::PaintOutlineSegments(q, outline, radius, segments); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp index 628c0ae4d5..36b57fd5d8 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_layout.cpp @@ -67,7 +67,7 @@ const auto kPsaBadgePrefix = "cloud_lng_badge_psa_"; } else if (const auto user = history->peer->asUser()) { return !user->lastseen().isHidden(); } - return !history->isForum(); + return !history->isForum() && !history->amMonoforumAdmin(); } void PaintRowTopRight( @@ -1046,21 +1046,23 @@ void RowPainter::Paint( ? nullptr : thread ? &thread->lastItemDialogsView() - : sublist - ? &sublist->lastItemDialogsView() : nullptr; if (view) { - const auto forum = context.st->topicsHeight - ? row->history()->peer->forum() + const auto forum = (peer && context.st->topicsHeight) + ? peer->forum() : nullptr; - if (!view->prepared(item, forum)) { + const auto monoforum = (peer && context.st->topicsHeight) + ? peer->monoforum() + : nullptr; + if (!view->prepared(item, forum, monoforum)) { view->prepare( item, forum, + monoforum, [=] { entry->updateChatListEntry(); }, {}); } - if (forum) { + if (forum || monoforum) { rect.setHeight(context.st->topicsHeight + rect.height()); } view->paint(p, rect, context); @@ -1154,8 +1156,13 @@ void RowPainter::Paint( availableWidth, st::dialogsTextFont->height); auto &view = row->itemView(); - if (!view.prepared(item, nullptr)) { - view.prepare(item, nullptr, row->repaint(), previewOptions); + if (!view.prepared(item, nullptr, nullptr)) { + view.prepare( + item, + nullptr, + nullptr, + row->repaint(), + previewOptions); } view.paint(p, itemRect, context); }; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp index b45708b844..87fd7232cb 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp @@ -138,26 +138,39 @@ bool MessageView::dependsOn(not_null item) const { bool MessageView::prepared( not_null item, - Data::Forum *forum) const { + Data::Forum *forum, + Data::SavedMessages *monoforum) const { return (_textCachedFor == item.get()) - && (!forum + && ((!forum && !monoforum) || (_topics && _topics->forum() == forum + && _topics->monoforum() == monoforum && _topics->prepared())); } void MessageView::prepare( not_null item, Data::Forum *forum, + Data::SavedMessages *monoforum, Fn customEmojiRepaint, ToPreviewOptions options) { - if (!forum) { + if (!forum && !monoforum) { _topics = nullptr; - } else if (!_topics || _topics->forum() != forum) { - _topics = std::make_unique(forum); - _topics->prepare(item->topicRootId(), customEmojiRepaint); + } else if (!_topics + || _topics->forum() != forum + || _topics->monoforum() != monoforum) { + _topics = std::make_unique(forum, monoforum); + if (forum) { + _topics->prepare(item->topicRootId(), customEmojiRepaint); + } else { + _topics->prepare(item->sublistPeerId(), customEmojiRepaint); + } } else if (!_topics->prepared()) { - _topics->prepare(item->topicRootId(), customEmojiRepaint); + if (forum) { + _topics->prepare(item->topicRootId(), customEmojiRepaint); + } else { + _topics->prepare(item->sublistPeerId(), customEmojiRepaint); + } } if (_textCachedFor == item.get()) { return; diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h index 4ee12aebd3..1cbe888a72 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h @@ -24,6 +24,7 @@ class SpoilerAnimation; namespace Data { class Forum; +class SavedMessages; } // namespace Data namespace HistoryView { @@ -56,10 +57,12 @@ public: [[nodiscard]] bool prepared( not_null item, - Data::Forum *forum) const; + Data::Forum *forum, + Data::SavedMessages *monoforum) const; void prepare( not_null item, Data::Forum *forum, + Data::SavedMessages *monoforum, Fn customEmojiRepaint, ToPreviewOptions options); diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp index 82fc64a7a9..ba42d90cc0 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp @@ -8,10 +8,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/ui/dialogs_topics_view.h" #include "dialogs/ui/dialogs_layout.h" +#include "data/stickers/data_custom_emoji.h" #include "data/data_forum.h" #include "data/data_forum_topic.h" +#include "data/data_peer.h" +#include "data/data_saved_messages.h" +#include "data/data_saved_sublist.h" +#include "data/data_session.h" #include "core/ui_integration.h" #include "lang/lang_keys.h" +#include "main/main_session.h" #include "ui/painter.h" #include "ui/power_saving.h" #include "ui/text/text_options.h" @@ -26,29 +32,35 @@ constexpr auto kIconLoopCount = 1; } // namespace -TopicsView::TopicsView(not_null forum) -: _forum(forum) { +TopicsView::TopicsView(Data::Forum *forum, Data::SavedMessages *monoforum) +: _forum(forum) +, _monoforum(monoforum) { } TopicsView::~TopicsView() = default; bool TopicsView::prepared() const { - return (_version == _forum->recentTopicsListVersion()); + const auto version = _forum + ? _forum->recentTopicsListVersion() + : _monoforum->recentSublistsListVersion(); + return (_version == version); } void TopicsView::prepare(MsgId frontRootId, Fn customEmojiRepaint) { + Expects(_forum != nullptr); + const auto &list = _forum->recentTopics(); _version = _forum->recentTopicsListVersion(); _titles.reserve(list.size()); auto index = 0; for (const auto &topic : list) { const auto from = begin(_titles) + index; - const auto rootId = topic->rootId(); + const auto key = topic->rootId().bare; const auto i = ranges::find( from, end(_titles), - rootId, - &Title::topicRootId); + key, + &Title::key); if (i != end(_titles)) { if (i != from) { ranges::rotate(from, i, i + 1); @@ -58,7 +70,7 @@ void TopicsView::prepare(MsgId frontRootId, Fn customEmojiRepaint) { } auto &title = _titles[index++]; const auto unread = topic->chatListBadgesState().unread; - if (title.topicRootId == rootId + if (title.key == key && title.unread == unread && title.version == topic->titleVersion()) { continue; @@ -69,7 +81,7 @@ void TopicsView::prepare(MsgId frontRootId, Fn customEmojiRepaint) { .customEmojiLoopLimit = kIconLoopCount, }); auto topicTitle = topic->titleWithIcon(); - title.topicRootId = rootId; + title.key = key; title.version = topic->titleVersion(); title.unread = unread; title.title.setMarkedText( @@ -87,7 +99,79 @@ void TopicsView::prepare(MsgId frontRootId, Fn customEmojiRepaint) { _titles.pop_back(); } const auto i = frontRootId - ? ranges::find(_titles, frontRootId, &Title::topicRootId) + ? ranges::find(_titles, frontRootId.bare, &Title::key) + : end(_titles); + _jumpToTopic = (i != end(_titles)); + if (_jumpToTopic) { + if (i != begin(_titles)) { + ranges::rotate(begin(_titles), i, i + 1); + } + if (!_titles.front().unread) { + _jumpToTopic = false; + } + } +} + +void TopicsView::prepare(PeerId frontPeerId, Fn customEmojiRepaint) { + Expects(_monoforum != nullptr); + + const auto &list = _monoforum->recentSublists(); + const auto manager = &_monoforum->session().data().customEmojiManager(); + _version = _monoforum->recentSublistsListVersion(); + _titles.reserve(list.size()); + auto index = 0; + for (const auto &sublist : list) { + const auto from = begin(_titles) + index; + const auto peer = sublist->sublistPeer(); + const auto key = peer->id.value; + const auto i = ranges::find( + from, + end(_titles), + key, + &Title::key); + if (i != end(_titles)) { + if (i != from) { + ranges::rotate(from, i, i + 1); + } + } else if (index >= _titles.size()) { + _titles.emplace_back(); + } + auto &title = _titles[index++]; + const auto unread = sublist->chatListBadgesState().unread; + if (title.key == key + && title.unread == unread + && title.version == peer->nameVersion()) { + continue; + } + const auto context = Core::TextContext({ + .session = &sublist->session(), + .repaint = customEmojiRepaint, + .customEmojiLoopLimit = kIconLoopCount, + }); + auto topicTitle = TextWithEntities().append( + Ui::Text::SingleCustomEmoji( + manager->peerUserpicEmojiData(peer), + u"@"_q) + ).append(peer->shortName()); + title.key = key; + title.version = peer->nameVersion(); + title.unread = unread; + title.title.setMarkedText( + st::dialogsTextStyle, + (unread + ? Ui::Text::Colorized( + Ui::Text::Wrapped( + std::move(topicTitle), + EntityType::Bold)) + : std::move(topicTitle)), + DialogTextOptions(), + context); + } + while (_titles.size() > index) { + _titles.pop_back(); + } + const auto i = frontPeerId + ? ranges::find(_titles, frontPeerId.value, &Title::key) : end(_titles); _jumpToTopic = (i != end(_titles)); if (_jumpToTopic) { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.h b/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.h index b90c3d5417..7c41337fad 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.h +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.h @@ -16,6 +16,8 @@ struct DialogRow; namespace Data { class Forum; class ForumTopic; +class SavedMessages; +class SavedSublist; } // namespace Data namespace Ui { @@ -59,15 +61,19 @@ void FillJumpToLastPrepared(QPainter &p, JumpToLastPrepared context); class TopicsView final { public: - explicit TopicsView(not_null forum); + TopicsView(Data::Forum *forum, Data::SavedMessages *monoforum); ~TopicsView(); - [[nodiscard]] not_null forum() const { + [[nodiscard]] Data::Forum *forum() const { return _forum; } + [[nodiscard]] Data::SavedMessages *monoforum() const { + return _monoforum; + } [[nodiscard]] bool prepared() const; void prepare(MsgId frontRootId, Fn customEmojiRepaint); + void prepare(PeerId frontPeerId, Fn customEmojiRepaint); [[nodiscard]] int jumpToTopicWidth() const; @@ -99,7 +105,7 @@ public: private: struct Title { Text::String title; - MsgId topicRootId = 0; + uint64 key = 0; int version = -1; bool unread = false; }; @@ -107,7 +113,8 @@ private: [[nodiscard]] QImage topicJumpRippleMask( not_null topicJumpCache) const; - const not_null _forum; + Data::Forum * const _forum = nullptr; + Data::SavedMessages * const _monoforum = nullptr; mutable std::vector _titles; mutable std::unique_ptr<RippleAnimation> _ripple; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 9e433de7c2..51bdcc9719 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -3149,11 +3149,11 @@ void History::monoforumChanged(Data::SavedMessages *old) { } if (const auto monoforum = peer->monoforum()) { - _flags |= Flag::IsMonoforum; + _flags |= Flag::IsMonoforumAdmin; monoforum->chatsList()->unreadStateChanges( ) | rpl::filter([=] { - return (_flags & Flag::IsMonoforum) && inChatList(); + return (_flags & Flag::IsMonoforumAdmin) && inChatList(); }) | rpl::map( AdjustedForumUnreadState ) | rpl::start_with_next([=](const Dialogs::UnreadState &old) { @@ -3165,7 +3165,7 @@ void History::monoforumChanged(Data::SavedMessages *old) { updateChatListEntry(); }, monoforum->lifetime()); } else { - _flags &= ~Flag::IsMonoforum; + _flags &= ~Flag::IsMonoforumAdmin; } if (cloudDraft(MsgId(0))) { updateChatListSortPosition(); @@ -3173,8 +3173,8 @@ void History::monoforumChanged(Data::SavedMessages *old) { _flags |= Flag::PendingAllItemsResize; } -bool History::isMonoforum() const { - return (_flags & Flag::IsMonoforum); +bool History::amMonoforumAdmin() const { + return (_flags & Flag::IsMonoforumAdmin); } not_null<History*> History::migrateToOrMe() const { diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index f8e0791d74..16fe57e351 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -74,7 +74,7 @@ public: [[nodiscard]] bool isForum() const; void monoforumChanged(Data::SavedMessages *old); - [[nodiscard]] bool isMonoforum() const; + [[nodiscard]] bool amMonoforumAdmin() const; [[nodiscard]] not_null<History*> migrateToOrMe() const; [[nodiscard]] History *migrateFrom() const; @@ -435,7 +435,7 @@ private: PendingAllItemsResize = (1 << 1), IsTopPromoted = (1 << 2), IsForum = (1 << 3), - IsMonoforum = (1 << 4), + IsMonoforumAdmin = (1 << 4), FakeUnreadWhileOpened = (1 << 5), HasPinnedMessages = (1 << 6), ResolveChatListMessage = (1 << 7), diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index b7f9305f33..42f9127032 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -3443,12 +3443,12 @@ FullStoryId HistoryItem::replyToStory() const { } FullReplyTo HistoryItem::replyTo() const { - const auto monoforumPeer = _history->peer->amMonoforumAdmin() - ? savedSublistPeer() - : nullptr; + const auto monoforumPeerId = _history->peer->amMonoforumAdmin() + ? sublistPeerId() + : PeerId(); auto result = FullReplyTo{ .topicRootId = topicRootId(), - .monoforumPeerId = monoforumPeer ? monoforumPeer->id : PeerId(), + .monoforumPeerId = monoforumPeerId, }; if (const auto reply = Get<HistoryMessageReply>()) { const auto &fields = reply->fields(); @@ -3592,11 +3592,15 @@ Data::SavedSublist *HistoryItem::savedSublist() const { return nullptr; } -PeerData *HistoryItem::savedSublistPeer() const { - if (const auto sublist = savedSublist()) { - return sublist->sublistPeer(); +PeerId HistoryItem::sublistPeerId() const { + if (const auto saved = Get<HistoryMessageSaved>()) { + return saved->sublistPeerId; + } else if (_history->peer->isSelf()) { + return _history->peer->id; + } else if (_history->peer->monoforum()) { + return _from->id; } - return nullptr; + return PeerId(); } PeerData *HistoryItem::savedFromSender() const { @@ -4046,8 +4050,8 @@ void HistoryItem::createComponentsHelper(HistoryItemCommonFields &&fields) { ? replyTo.messageId.peer : PeerId(); const auto to = LookupReplyTo(_history, replyTo.messageId); - config.reply.monoforumPeerId = (to && to->savedSublistPeer()) - ? to->savedSublistPeer()->id + config.reply.monoforumPeerId = (to && to->sublistPeerId()) + ? to->sublistPeerId() : replyTo.monoforumPeerId ? replyTo.monoforumPeerId : PeerId(); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index fcfd616382..6a64ccfe4c 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -491,7 +491,7 @@ public: [[nodiscard]] MsgId originalId() const; [[nodiscard]] Data::SavedSublist *savedSublist() const; - [[nodiscard]] PeerData *savedSublistPeer() const; + [[nodiscard]] PeerId sublistPeerId() const; [[nodiscard]] PeerData *savedFromSender() const; [[nodiscard]] const HiddenSenderInfo *savedFromHiddenSenderInfo() const; diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp index 7bada5f52e..d789f7f07b 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp @@ -1699,10 +1699,10 @@ FullReplyTo ChatWidget::replyTo() const { const auto item = custom.messageId ? session().data().message(custom.messageId) : nullptr; - const auto sublistPeer = item ? item->savedSublistPeer() : nullptr; + const auto sublistPeerId = item ? item->sublistPeerId() : PeerId(); if (!item || !monoforumPeerId - || (sublistPeer && sublistPeer->id == monoforumPeerId)) { + || (sublistPeerId == monoforumPeerId)) { // Never answer to a message in a wrong monoforum peer id. custom.topicRootId = _repliesRootId; custom.monoforumPeerId = monoforumPeerId; diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index eec58baf5c..b89b170eb6 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -1487,7 +1487,7 @@ void Element::recountMonoforumSenderBarInBlocks() { } } } - return sublistPeer; + return (sublistPeer == parentChat) ? nullptr : sublistPeer.get(); }(); if (barPeer && !Has<MonoforumSenderBar>()) { AddComponents(MonoforumSenderBar::Bit()); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index d0e2aca0e6..b93e55033e 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -773,7 +773,7 @@ void TopBarWidget::backClicked() { _controller->closeForum(); } else if (_activeChat.section == Section::ChatsList && _activeChat.key.history() - && _activeChat.key.history()->isMonoforum()) { + && _activeChat.key.history()->amMonoforumAdmin()) { _controller->closeMonoforum(); } else { _controller->showBackFromStack(); @@ -1236,7 +1236,8 @@ void TopBarWidget::updateMembersShowArea() { } else if (const auto chat = peer->asChat()) { return chat->amIn(); } else if (const auto megagroup = peer->asMegagroup()) { - return megagroup->canViewMembers() + return !megagroup->isMonoforum() + && megagroup->canViewMembers() && (megagroup->membersCount() < megagroup->session().serverConfig().chatSizeMax); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp index bc7de9ef5c..dd760a3e07 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_actions.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_actions.cpp @@ -1777,9 +1777,14 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupPersonalChannel( style::al_left); return; } - if (!state->view.prepared(item, nullptr)) { + if (!state->view.prepared(item, nullptr, nullptr)) { const auto repaint = [=] { preview->update(); }; - state->view.prepare(item, nullptr, repaint, {}); + state->view.prepare( + item, + nullptr, + nullptr, + repaint, + {}); } state->view.paint(p, preview->rect(), { .st = &st::defaultDialogRow,