From bc9c413e824f40242fd60d9d276a238ead8a2eda Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 17 Jul 2026 16:19:08 +0400 Subject: [PATCH] Open a community chats chooser when forwarding. --- .../boxes/peer_list_controllers.cpp | 192 ++++++++++++++++++ .../SourceFiles/boxes/peer_list_controllers.h | 27 +++ 2 files changed, 219 insertions(+) diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 3d62f96e18..84c14e1ee2 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_stories.h" #include "data/data_channel.h" #include "data/data_chat.h" +#include "data/data_community.h" #include "data/data_user.h" #include "data/data_forum.h" #include "data/data_forum_topic.h" @@ -61,6 +62,15 @@ namespace { constexpr auto kSortByOnlineThrottle = 3 * crl::time(1000); constexpr auto kSearchPerPage = 50; +[[nodiscard]] Data::CommunityInfo *JoinedCommunityChats( + not_null peer) { + const auto channel = peer->asChannel(); + const auto info = (channel && channel->isCommunity()) + ? channel->communityInfo() + : nullptr; + return (info && !info->histories().empty()) ? info : nullptr; +} + } // namespace object_ptr PrepareContactsBox( @@ -957,6 +967,50 @@ void ChooseRecipientBoxController::rowClicked(not_null row) { *weak = owned.data(); delegate()->peerListUiShow()->showBox(std::move(owned)); return; + } else if (const auto community = JoinedCommunityChats(peer)) { + const auto weak = std::make_shared>(); + auto callback = [=](not_null thread) { + const auto exists = guard.get(); + if (!exists) { + if (*weak) { + (*weak)->closeBox(); + } + return; + } + auto onstack = std::move(_callback); + onstack(thread); + if (guard) { + _callback = std::move(onstack); + } else if (*weak) { + (*weak)->closeBox(); + } + }; + const auto filter = [=](not_null thread) { + return guard && (!_filter || _filter(thread)); + }; + const auto channel = community->channel(); + auto owned = Box( + std::make_unique( + community, + std::move(callback), + filter), + [=](not_null box) { + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); + + channel->flagsValue( + ) | rpl::filter([=](const ChannelData::Flags::Change &update) { + using Flag = ChannelData::Flag; + return (update.diff & Flag::Community) + && !(update.value & Flag::Community); + }) | rpl::on_next([=] { + box->closeBox(); + }, box->lifetime()); + }); + *weak = owned.data(); + delegate()->peerListUiShow()->showBox(std::move(owned)); + return; } const auto history = peer->owner().history(peer); auto callback = std::move(_callback); @@ -1397,6 +1451,144 @@ auto ChooseSublistBoxController::createRow( return result; }; +ChooseCommunityChatBoxController::ChooseCommunityChatBoxController( + not_null community, + FnMut)> callback, + Fn)> filter) +: _community(community) +, _callback(std::move(callback)) +, _filter(std::move(filter)) { + setStyleOverrides(&st::chooseTopicList); + + _community->linkedPeersValue( + ) | rpl::skip(1) | rpl::on_next([=] { + refreshRows(); + }, lifetime()); +} + +Main::Session &ChooseCommunityChatBoxController::session() const { + return _community->channel()->session(); +} + +void ChooseCommunityChatBoxController::rowClicked(not_null row) { + auto guard = base::make_weak(this); + const auto peer = row->peer(); + if (const auto forum = peer->forum()) { + const auto weak = std::make_shared>(); + auto callback = [=](not_null thread) { + const auto exists = guard.get(); + if (!exists) { + if (*weak) { + (*weak)->closeBox(); + } + return; + } + auto onstack = std::move(_callback); + onstack(thread); + if (guard) { + _callback = std::move(onstack); + } else if (*weak) { + (*weak)->closeBox(); + } + }; + const auto filter = [=](not_null thread) { + return guard && (!_filter || _filter(thread)); + }; + auto owned = Box( + std::make_unique( + forum, + std::move(callback), + filter), + [=](not_null box) { + box->addButton(tr::lng_cancel(), [=] { + box->closeBox(); + }); + + forum->destroyed( + ) | rpl::on_next([=] { + box->closeBox(); + }, box->lifetime()); + }); + *weak = owned.data(); + delegate()->peerListUiShow()->showBox(std::move(owned)); + return; + } + auto onstack = base::take(_callback); + onstack(peer->owner().history(peer)); + if (guard) { + _callback = std::move(onstack); + } +} + +void ChooseCommunityChatBoxController::prepare() { + delegate()->peerListSetTitle(tr::lng_forward_choose()); + setSearchNoResultsText(tr::lng_topics_not_found(tr::now)); + delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled); + refreshRows(true); +} + +void ChooseCommunityChatBoxController::refreshRows(bool initial) { + auto changed = false; + const auto &histories = _community->histories(); + const auto owner = &session().data(); + for (auto i = 0; i != delegate()->peerListFullRowsCount();) { + const auto row = delegate()->peerListRowAt(i); + const auto history = owner->history(row->peer()); + if (histories.contains(history)) { + ++i; + } else { + delegate()->peerListRemoveRow(row); + changed = true; + } + } + auto sorted = std::vector>( + begin(histories), + end(histories)); + ranges::sort(sorted, [](not_null a, not_null b) { + const auto aDate = a->chatListTimeId(); + const auto bDate = b->chatListTimeId(); + return (aDate != bDate) + ? (aDate > bDate) + : (a->peer->name() < b->peer->name()); + }); + for (const auto &history : sorted) { + const auto id = history->peer->id.value; + auto already = delegate()->peerListFindRow(id); + if (initial || !already) { + if (auto created = createRow(history)) { + delegate()->peerListAppendRow(std::move(created)); + changed = true; + } + } else if (already->isSearchResult()) { + delegate()->peerListAppendFoundRow(already); + changed = true; + } + } + if (changed) { + delegate()->peerListRefreshRows(); + } +} + +std::unique_ptr ChooseCommunityChatBoxController::createSearchRow( + PeerListRowId id) { + const auto peer = session().data().peer(PeerId(id)); + const auto history = peer->owner().historyLoaded(peer); + if (history && _community->histories().contains(history)) { + return createRow(history); + } + return nullptr; +} + +auto ChooseCommunityChatBoxController::createRow(not_null history) +-> std::unique_ptr { + if (_filter && !_filter(history)) { + return nullptr; + } + auto result = std::make_unique(history->peer); + result->setCustomStatus(QString()); + return result; +} + void PaintRestrictionBadge( Painter &p, not_null st, diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index c110499c48..87484fe822 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -25,6 +25,7 @@ struct MessageMoneyRestriction; namespace Data { class Thread; +class CommunityInfo; class Forum; class ForumTopic; class SavedSublist; @@ -453,3 +454,29 @@ private: Fn)> _filter; }; + +class ChooseCommunityChatBoxController final + : public PeerListController + , public base::has_weak_ptr { +public: + ChooseCommunityChatBoxController( + not_null community, + FnMut)> callback, + Fn)> filter = nullptr); + + Main::Session &session() const override; + void rowClicked(not_null row) override; + + void prepare() override; + std::unique_ptr createSearchRow(PeerListRowId id) override; + +private: + void refreshRows(bool initial = false); + [[nodiscard]] std::unique_ptr createRow( + not_null history); + + const not_null _community; + FnMut)> _callback; + Fn)> _filter; + +};