Support similar bots section.

This commit is contained in:
John Preston 2025-01-10 11:26:07 +04:00
parent 232077b919
commit 6ff5e221ea
17 changed files with 226 additions and 157 deletions

View file

@ -1014,8 +1014,8 @@ PRIVATE
info/saved/info_saved_sublists_widget.h info/saved/info_saved_sublists_widget.h
info/settings/info_settings_widget.cpp info/settings/info_settings_widget.cpp
info/settings/info_settings_widget.h info/settings/info_settings_widget.h
info/similar_channels/info_similar_channels_widget.cpp info/similar_peers/info_similar_peers_widget.cpp
info/similar_channels/info_similar_channels_widget.h info/similar_peers/info_similar_peers_widget.h
info/statistics/info_statistics_common.h info/statistics/info_statistics_common.h
info/statistics/info_statistics_inner_widget.cpp info/statistics/info_statistics_inner_widget.cpp
info/statistics/info_statistics_inner_widget.h info/statistics/info_statistics_inner_widget.h

View file

@ -1346,6 +1346,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_common_groups#other" = "{count} groups in common"; "lng_profile_common_groups#other" = "{count} groups in common";
"lng_profile_similar_channels#one" = "{count} similar channel"; "lng_profile_similar_channels#one" = "{count} similar channel";
"lng_profile_similar_channels#other" = "{count} similar channels"; "lng_profile_similar_channels#other" = "{count} similar channels";
"lng_profile_similar_bots#one" = "{count} similar bot";
"lng_profile_similar_bots#other" = "{count} similar bots";
"lng_profile_saved_messages#one" = "{count} saved message"; "lng_profile_saved_messages#one" = "{count} saved message";
"lng_profile_saved_messages#other" = "{count} saved messages"; "lng_profile_saved_messages#other" = "{count} saved messages";
"lng_profile_peer_gifts#one" = "{count} gift"; "lng_profile_peer_gifts#one" = "{count} gift";
@ -2108,6 +2110,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_similar_channels_premium_all_link" = "Telegram Premium"; "lng_similar_channels_premium_all_link" = "Telegram Premium";
"lng_similar_channels_show_more" = "Show more channels"; "lng_similar_channels_show_more" = "Show more channels";
"lng_similar_bots_title" = "Similar bots";
"lng_similar_bots_premium_all#one" = "Subscribe to {link} to unlock up to **{count}** similar bot.";
"lng_similar_bots_premium_all#other" = "Subscribe to {link} to unlock up to **{count}** similar bots.";
"lng_similar_bots_show_more" = "Show more bots";
"lng_peer_gifts_title" = "Gifts"; "lng_peer_gifts_title" = "Gifts";
"lng_peer_gifts_about" = "These gifts were sent to {user} by other users."; "lng_peer_gifts_about" = "These gifts were sent to {user} by other users.";
"lng_peer_gifts_about_mine" = "These gifts were sent to you by other users. Click on a gift to convert it to Stars or change its privacy settings."; "lng_peer_gifts_about_mine" = "These gifts were sent to you by other users. Click on a gift to convert it to Stars or change its privacy settings.";

View file

@ -211,11 +211,10 @@ void ApplyBotsList(
Data::PeerUpdate::Flag::FullInfo); Data::PeerUpdate::Flag::FullInfo);
} }
[[nodiscard]] ChatParticipants::Channels ParseSimilar( [[nodiscard]] ChatParticipants::Peers ParseSimilarChannels(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const MTPmessages_Chats &chats) { const MTPmessages_Chats &chats) {
auto result = ChatParticipants::Channels(); auto result = ChatParticipants::Peers();
std::vector<not_null<ChannelData*>>();
chats.match([&](const auto &data) { chats.match([&](const auto &data) {
const auto &list = data.vchats().v; const auto &list = data.vchats().v;
result.list.reserve(list.size()); result.list.reserve(list.size());
@ -234,10 +233,29 @@ void ApplyBotsList(
return result; return result;
} }
[[nodiscard]] ChatParticipants::Channels ParseSimilar( [[nodiscard]] ChatParticipants::Peers ParseSimilarChannels(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
const MTPmessages_Chats &chats) { const MTPmessages_Chats &chats) {
return ParseSimilar(&channel->session(), chats); return ParseSimilarChannels(&channel->session(), chats);
}
[[nodiscard]] ChatParticipants::Peers ParseSimilarBots(
not_null<Main::Session*> session,
const MTPusers_Users &users) {
auto result = ChatParticipants::Peers();
users.match([&](const auto &data) {
const auto &list = data.vusers().v;
result.list.reserve(list.size());
for (const auto &user : list) {
result.list.push_back(session->data().processUser(user));
}
if constexpr (MTPDusers_usersSlice::Is<decltype(data)>()) {
if (session->premiumPossible()) {
result.more = data.vcount().v - data.vusers().v.size();
}
}
});
return result;
} }
} // namespace } // namespace
@ -782,29 +800,28 @@ void ChatParticipants::unblock(
_kickRequests.emplace(kick, requestId); _kickRequests.emplace(kick, requestId);
} }
void ChatParticipants::loadSimilarChannels(not_null<ChannelData*> channel) { void ChatParticipants::loadSimilarPeers(not_null<PeerData*> peer) {
if (!channel->isBroadcast()) { if (const auto i = _similar.find(peer); i != end(_similar)) {
return;
} else if (const auto i = _similar.find(channel); i != end(_similar)) {
if (i->second.requestId if (i->second.requestId
|| !i->second.channels.more || !i->second.peers.more
|| !channel->session().premium()) { || !peer->session().premium()) {
return; return;
} }
} }
if (const auto channel = peer->asBroadcast()) {
using Flag = MTPchannels_GetChannelRecommendations::Flag; using Flag = MTPchannels_GetChannelRecommendations::Flag;
_similar[channel].requestId = _api.request( _similar[peer].requestId = _api.request(
MTPchannels_GetChannelRecommendations( MTPchannels_GetChannelRecommendations(
MTP_flags(Flag::f_channel), MTP_flags(Flag::f_channel),
channel->inputChannel) channel->inputChannel)
).done([=](const MTPmessages_Chats &result) { ).done([=](const MTPmessages_Chats &result) {
auto &similar = _similar[channel]; auto &similar = _similar[channel];
similar.requestId = 0; similar.requestId = 0;
auto parsed = ParseSimilar(channel, result); auto parsed = ParseSimilarChannels(channel, result);
if (similar.channels == parsed) { if (similar.peers == parsed) {
return; return;
} }
similar.channels = std::move(parsed); similar.peers = std::move(parsed);
if (const auto history = channel->owner().historyLoaded(channel)) { if (const auto history = channel->owner().historyLoaded(channel)) {
if (const auto item = history->joinedMessageInstance()) { if (const auto item = history->joinedMessageInstance()) {
history->owner().requestItemResize(item); history->owner().requestItemResize(item);
@ -812,22 +829,36 @@ void ChatParticipants::loadSimilarChannels(not_null<ChannelData*> channel) {
} }
_similarLoaded.fire_copy(channel); _similarLoaded.fire_copy(channel);
}).send(); }).send();
} else if (const auto bot = peer->asBot()) {
_similar[peer].requestId = _api.request(
MTPbots_GetBotRecommendations(bot->inputUser)
).done([=](const MTPusers_Users &result) {
auto &similar = _similar[peer];
similar.requestId = 0;
auto parsed = ParseSimilarBots(&peer->session(), result);
if (similar.peers == parsed) {
return;
}
similar.peers = std::move(parsed);
_similarLoaded.fire_copy(peer);
}).send();
}
} }
auto ChatParticipants::similar(not_null<ChannelData*> channel) auto ChatParticipants::similar(not_null<PeerData*> peer)
-> const Channels & { -> const Peers & {
const auto i = channel->isBroadcast() const auto i = (peer->isBroadcast() || peer->isBot())
? _similar.find(channel) ? _similar.find(peer)
: end(_similar); : end(_similar);
if (i != end(_similar)) { if (i != end(_similar)) {
return i->second.channels; return i->second.peers;
} }
static const auto empty = Channels(); static const auto empty = Peers();
return empty; return empty;
} }
auto ChatParticipants::similarLoaded() const auto ChatParticipants::similarLoaded() const
-> rpl::producer<not_null<ChannelData*>> { -> rpl::producer<not_null<PeerData*>> {
return _similarLoaded.events(); return _similarLoaded.events();
} }
@ -841,15 +872,15 @@ void ChatParticipants::loadRecommendations() {
MTP_inputChannelEmpty()) MTP_inputChannelEmpty())
).done([=](const MTPmessages_Chats &result) { ).done([=](const MTPmessages_Chats &result) {
_recommendations.requestId = 0; _recommendations.requestId = 0;
auto parsed = ParseSimilar(_session, result); auto parsed = ParseSimilarChannels(_session, result);
_recommendations.channels = std::move(parsed); _recommendations.peers = std::move(parsed);
_recommendations.channels.more = 0; _recommendations.peers.more = 0;
_recommendationsLoaded = true; _recommendationsLoaded = true;
}).send(); }).send();
} }
const ChatParticipants::Channels &ChatParticipants::recommendations() const { const ChatParticipants::Peers &ChatParticipants::recommendations() const {
return _recommendations.channels; return _recommendations.peers;
} }
rpl::producer<> ChatParticipants::recommendationsLoaded() const { rpl::producer<> ChatParticipants::recommendationsLoaded() const {

View file

@ -138,27 +138,27 @@ public:
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
not_null<PeerData*> participant); not_null<PeerData*> participant);
void loadSimilarChannels(not_null<ChannelData*> channel); void loadSimilarPeers(not_null<PeerData*> peer);
struct Channels { struct Peers {
std::vector<not_null<ChannelData*>> list; std::vector<not_null<PeerData*>> list;
int more = 0; int more = 0;
friend inline bool operator==( friend inline bool operator==(
const Channels &, const Peers &,
const Channels &) = default; const Peers &) = default;
}; };
[[nodiscard]] const Channels &similar(not_null<ChannelData*> channel); [[nodiscard]] const Peers &similar(not_null<PeerData*> peer);
[[nodiscard]] auto similarLoaded() const [[nodiscard]] auto similarLoaded() const
-> rpl::producer<not_null<ChannelData*>>; -> rpl::producer<not_null<PeerData*>>;
void loadRecommendations(); void loadRecommendations();
[[nodiscard]] const Channels &recommendations() const; [[nodiscard]] const Peers &recommendations() const;
[[nodiscard]] rpl::producer<> recommendationsLoaded() const; [[nodiscard]] rpl::producer<> recommendationsLoaded() const;
private: private:
struct SimilarChannels { struct SimilarPeers {
Channels channels; Peers peers;
mtpRequestId requestId = 0; mtpRequestId requestId = 0;
}; };
@ -186,10 +186,10 @@ private:
not_null<PeerData*>>; not_null<PeerData*>>;
base::flat_map<KickRequest, mtpRequestId> _kickRequests; base::flat_map<KickRequest, mtpRequestId> _kickRequests;
base::flat_map<not_null<ChannelData*>, SimilarChannels> _similar; base::flat_map<not_null<PeerData*>, SimilarPeers> _similar;
rpl::event_stream<not_null<ChannelData*>> _similarLoaded; rpl::event_stream<not_null<PeerData*>> _similarLoaded;
SimilarChannels _recommendations; SimilarPeers _recommendations;
rpl::variable<bool> _recommendationsLoaded = false; rpl::variable<bool> _recommendationsLoaded = false;
}; };

View file

@ -1772,7 +1772,7 @@ void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
_channelAmInRequests.emplace(channel, requestId); _channelAmInRequests.emplace(channel, requestId);
using Flag = ChannelDataFlag; using Flag = ChannelDataFlag;
chatParticipants().loadSimilarChannels(channel); chatParticipants().loadSimilarPeers(channel);
channel->setFlags(channel->flags() | Flag::SimilarExpanded); channel->setFlags(channel->flags() | Flag::SimilarExpanded);
} }
} }

View file

@ -911,6 +911,16 @@ void PeerData::fullUpdated() {
setLoadedStatus(LoadedStatus::Full); setLoadedStatus(LoadedStatus::Full);
} }
UserData *PeerData::asBot() {
return isBot() ? static_cast<UserData*>(this) : nullptr;
}
const UserData *PeerData::asBot() const {
return isBot()
? static_cast<const UserData*>(this)
: nullptr;
}
UserData *PeerData::asUser() { UserData *PeerData::asUser() {
return isUser() ? static_cast<UserData*>(this) : nullptr; return isUser() ? static_cast<UserData*>(this) : nullptr;
} }
@ -1131,6 +1141,13 @@ EmojiStatusId PeerData::emojiStatusId() const {
return _emojiStatusId; return _emojiStatusId;
} }
bool PeerData::isBot() const {
if (const auto user = asUser()) {
return user->isBot();
}
return false;
}
bool PeerData::isSelf() const { bool PeerData::isSelf() const {
if (const auto user = asUser()) { if (const auto user = asUser()) {
return (user->flags() & UserDataFlag::Self); return (user->flags() & UserDataFlag::Self);

View file

@ -218,6 +218,7 @@ public:
[[nodiscard]] bool isChannel() const { [[nodiscard]] bool isChannel() const {
return peerIsChannel(id); return peerIsChannel(id);
} }
[[nodiscard]] bool isBot() const;
[[nodiscard]] bool isSelf() const; [[nodiscard]] bool isSelf() const;
[[nodiscard]] bool isVerified() const; [[nodiscard]] bool isVerified() const;
[[nodiscard]] bool isPremium() const; [[nodiscard]] bool isPremium() const;
@ -267,6 +268,8 @@ public:
[[nodiscard]] int slowmodeSecondsLeft() const; [[nodiscard]] int slowmodeSecondsLeft() const;
[[nodiscard]] bool canManageGroupCall() const; [[nodiscard]] bool canManageGroupCall() const;
[[nodiscard]] UserData *asBot();
[[nodiscard]] const UserData *asBot() const;
[[nodiscard]] UserData *asUser(); [[nodiscard]] UserData *asUser();
[[nodiscard]] const UserData *asUser() const; [[nodiscard]] const UserData *asUser() const;
[[nodiscard]] ChatData *asChat(); [[nodiscard]] ChatData *asChat();

View file

@ -18,7 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "info/similar_channels/info_similar_channels_widget.h" #include "info/similar_peers/info_similar_peers_widget.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "info/info_memento.h" #include "info/info_memento.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -37,7 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView { namespace HistoryView {
namespace { namespace {
using Channels = Api::ChatParticipants::Channels; using Channels = Api::ChatParticipants::Peers;
//void SimilarChannelsController::prepare() { //void SimilarChannelsController::prepare() {
// for (const auto &channel : _channels.list) { // for (const auto &channel : _channels.list) {
@ -86,7 +86,7 @@ using Channels = Api::ChatParticipants::Channels;
strong->showSection( strong->showSection(
std::make_shared<Info::Memento>( std::make_shared<Info::Memento>(
channel, channel,
Info::Section::Type::SimilarChannels)); Info::Section::Type::SimilarPeers));
} }
}); });
} }
@ -519,7 +519,7 @@ QSize SimilarChannels::countOptimalSize() {
_channels.clear(); _channels.clear();
_moreThumbnails = {}; _moreThumbnails = {};
const auto api = &channel->session().api(); const auto api = &channel->session().api();
api->chatParticipants().loadSimilarChannels(channel); api->chatParticipants().loadSimilarPeers(channel);
const auto premium = channel->session().premium(); const auto premium = channel->session().premium();
const auto &similar = api->chatParticipants().similar(channel); const auto &similar = api->chatParticipants().similar(channel);
_empty = similar.list.empty() ? 1 : 0; _empty = similar.list.empty() ? 1 : 0;
@ -543,9 +543,14 @@ QSize SimilarChannels::countOptimalSize() {
? limit ? limit
: int(similar.list.size()); : int(similar.list.size());
const auto more = similar.more + int(similar.list.size() - take); const auto more = similar.more + int(similar.list.size() - take);
auto &&channels = ranges::views::all(similar.list) auto &&peers = ranges::views::all(similar.list)
| ranges::views::take(limit); | ranges::views::take(limit);
for (const auto &channel : channels) { for (const auto &peer : peers) {
const auto channel = peer->asBroadcast();
if (!channel) {
continue;
}
const auto moreCounter = (_channels.size() + 1 == take) ? more : 0; const auto moreCounter = (_channels.size() + 1 == take) ? more : 0;
_channels.push_back({ _channels.push_back({
.geometry = QRect(QPoint(x, y), outer.size()), .geometry = QRect(QPoint(x, y), outer.size()),
@ -566,8 +571,8 @@ QSize SimilarChannels::countOptimalSize() {
: channel->openLink(); : channel->openLink();
const auto counter = moreCounter const auto counter = moreCounter
? moreCounter : ? moreCounter
channel->membersCount(); : channel->membersCount();
if (moreCounter || counter > 1) { if (moreCounter || counter > 1) {
last.counter = (moreCounter ? u"+"_q : QString()) last.counter = (moreCounter ? u"+"_q : QString())
+ Lang::FormatCountToShort(counter).string; + Lang::FormatCountToShort(counter).string;

View file

@ -477,6 +477,7 @@ infoIconMediaAudio: icon {{ "info/info_media_audio", infoIconFg }};
infoIconMediaLink: icon {{ "info/info_media_link", infoIconFg }}; infoIconMediaLink: icon {{ "info/info_media_link", infoIconFg }};
infoIconMediaGroup: icon {{ "info/info_common_groups", infoIconFg }}; infoIconMediaGroup: icon {{ "info/info_common_groups", infoIconFg }};
infoIconMediaChannel: icon {{ "menu/channel", infoIconFg, point(4px, 4px) }}; infoIconMediaChannel: icon {{ "menu/channel", infoIconFg, point(4px, 4px) }};
infoIconMediaBot: icon {{ "menu/bot", infoIconFg, point(4px, 4px) }};
infoIconMediaVoice: icon {{ "info/info_media_voice", infoIconFg }}; infoIconMediaVoice: icon {{ "info/info_media_voice", infoIconFg }};
infoIconMediaStories: icon {{ "info/info_media_stories", infoIconFg }}; infoIconMediaStories: icon {{ "info/info_media_stories", infoIconFg }};
infoIconMediaSaved: icon {{ "info/info_media_saved", infoIconFg }}; infoIconMediaSaved: icon {{ "info/info_media_saved", infoIconFg }};

View file

@ -158,7 +158,7 @@ public:
Media, Media,
GlobalMedia, GlobalMedia,
CommonGroups, CommonGroups,
SimilarChannels, SimilarPeers,
RequestsList, RequestsList,
ReactionsList, ReactionsList,
SavedSublists, SavedSublists,

View file

@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/common_groups/info_common_groups_widget.h" #include "info/common_groups/info_common_groups_widget.h"
#include "info/saved/info_saved_sublists_widget.h" #include "info/saved/info_saved_sublists_widget.h"
#include "info/settings/info_settings_widget.h" #include "info/settings/info_settings_widget.h"
#include "info/similar_channels/info_similar_channels_widget.h" #include "info/similar_peers/info_similar_peers_widget.h"
#include "info/reactions_list/info_reactions_list_widget.h" #include "info/reactions_list/info_reactions_list_widget.h"
#include "info/requests_list/info_requests_list_widget.h" #include "info/requests_list/info_requests_list_widget.h"
#include "info/peer_gifts/info_peer_gifts_widget.h" #include "info/peer_gifts/info_peer_gifts_widget.h"
@ -172,9 +172,8 @@ std::shared_ptr<ContentMemento> Memento::DefaultContent(
section.mediaType()); section.mediaType());
case Section::Type::CommonGroups: case Section::Type::CommonGroups:
return std::make_shared<CommonGroups::Memento>(peer->asUser()); return std::make_shared<CommonGroups::Memento>(peer->asUser());
case Section::Type::SimilarChannels: case Section::Type::SimilarPeers:
return std::make_shared<SimilarChannels::Memento>( return std::make_shared<SimilarPeers::Memento>(peer);
peer->asChannel());
case Section::Type::RequestsList: case Section::Type::RequestsList:
return std::make_shared<RequestsList::Memento>(peer); return std::make_shared<RequestsList::Memento>(peer);
case Section::Type::PeerGifts: case Section::Type::PeerGifts:

View file

@ -131,26 +131,25 @@ inline auto AddCommonGroupsButton(
return result; return result;
} }
inline auto AddSimilarChannelsButton( inline auto AddSimilarPeersButton(
Ui::VerticalLayout *parent, Ui::VerticalLayout *parent,
not_null<Window::SessionNavigation*> navigation, not_null<Window::SessionNavigation*> navigation,
not_null<ChannelData*> channel, not_null<PeerData*> peer,
Ui::MultiSlideTracker &tracker) { Ui::MultiSlideTracker &tracker) {
auto result = AddCountedButton( auto result = AddCountedButton(
parent, parent,
Profile::SimilarChannelsCountValue(channel), Profile::SimilarPeersCountValue(peer),
[](int count) { [=](int count) {
return tr::lng_profile_similar_channels( return peer->isBroadcast()
tr::now, ? tr::lng_profile_similar_channels(tr::now, lt_count, count)
lt_count, : tr::lng_profile_similar_bots(tr::now, lt_count, count);
count);
}, },
tracker)->entity(); tracker)->entity();
result->addClickHandler([=] { result->addClickHandler([=] {
navigation->showSection( navigation->showSection(
std::make_shared<Info::Memento>( std::make_shared<Info::Memento>(
channel, peer,
Section::Type::SimilarChannels)); Section::Type::SimilarPeers));
}); });
return result; return result;
} }

View file

@ -178,13 +178,13 @@ object_ptr<Ui::RpWidget> InnerWidget::setupSharedMedia(
icon, icon,
st::infoSharedMediaButtonIconPosition); st::infoSharedMediaButtonIconPosition);
}; };
const auto addSimilarChannelsButton = [&]( const auto addSimilarPeersButton = [&](
not_null<ChannelData*> channel, not_null<PeerData*> peer,
const style::icon &icon) { const style::icon &icon) {
auto result = Media::AddSimilarChannelsButton( auto result = Media::AddSimilarPeersButton(
content, content,
_controller, _controller,
channel, peer,
tracker); tracker);
object_ptr<Profile::FloatingIcon>( object_ptr<Profile::FloatingIcon>(
result, result,
@ -248,10 +248,12 @@ object_ptr<Ui::RpWidget> InnerWidget::setupSharedMedia(
addMediaButton(MediaType::Link, st::infoIconMediaLink); addMediaButton(MediaType::Link, st::infoIconMediaLink);
addMediaButton(MediaType::RoundVoiceFile, st::infoIconMediaVoice); addMediaButton(MediaType::RoundVoiceFile, st::infoIconMediaVoice);
addMediaButton(MediaType::GIF, st::infoIconMediaGif); addMediaButton(MediaType::GIF, st::infoIconMediaGif);
if (const auto user = _peer->asUser()) { if (const auto bot = _peer->asBot()) {
addSimilarPeersButton(bot, st::infoIconMediaBot);
} else if (const auto channel = _peer->asBroadcast()) {
addSimilarPeersButton(channel, st::infoIconMediaChannel);
} else if (const auto user = _peer->asUser()) {
addCommonGroupsButton(user, st::infoIconMediaGroup); addCommonGroupsButton(user, st::infoIconMediaGroup);
} else if (const auto channel = _peer->asChannel()) {
addSimilarChannelsButton(channel, st::infoIconMediaChannel);
} }
auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( auto result = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(

View file

@ -570,16 +570,16 @@ rpl::producer<int> CommonGroupsCountValue(not_null<UserData*> user) {
}); });
} }
rpl::producer<int> SimilarChannelsCountValue( rpl::producer<int> SimilarPeersCountValue(
not_null<ChannelData*> channel) { not_null<PeerData*> peer) {
const auto participants = &channel->session().api().chatParticipants(); const auto participants = &peer->session().api().chatParticipants();
participants->loadSimilarChannels(channel); participants->loadSimilarPeers(peer);
return rpl::single(channel) | rpl::then( return rpl::single(peer) | rpl::then(
participants->similarLoaded() participants->similarLoaded()
) | rpl::filter( ) | rpl::filter(
rpl::mappers::_1 == channel rpl::mappers::_1 == peer
) | rpl::map([=] { ) | rpl::map([=] {
const auto &similar = participants->similar(channel); const auto &similar = participants->similar(peer);
return int(similar.list.size()) + similar.more; return int(similar.list.size()) + similar.more;
}); });
} }

View file

@ -117,8 +117,8 @@ struct LinkWithUrl {
Storage::SharedMediaType type); Storage::SharedMediaType type);
[[nodiscard]] rpl::producer<int> CommonGroupsCountValue( [[nodiscard]] rpl::producer<int> CommonGroupsCountValue(
not_null<UserData*> user); not_null<UserData*> user);
[[nodiscard]] rpl::producer<int> SimilarChannelsCountValue( [[nodiscard]] rpl::producer<int> SimilarPeersCountValue(
not_null<ChannelData*> channel); not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<int> SavedSublistCountValue( [[nodiscard]] rpl::producer<int> SavedSublistCountValue(
not_null<PeerData*> peer); not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<int> PeerGiftsCountValue( [[nodiscard]] rpl::producer<int> PeerGiftsCountValue(

View file

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link: For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "info/similar_channels/info_similar_channels_widget.h" #include "info/similar_peers/info_similar_peers_widget.h"
#include "api/api_chat_participants.h" #include "api/api_chat_participants.h"
#include "apiwrap.h" #include "apiwrap.h"
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_premium_limits.h" #include "data/data_premium_limits.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h"
#include "info/info_controller.h" #include "info/info_controller.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
@ -27,14 +28,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_info.h" #include "styles/style_info.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
namespace Info::SimilarChannels { namespace Info::SimilarPeers {
namespace { namespace {
class ListController final : public PeerListController { class ListController final : public PeerListController {
public: public:
ListController( ListController(
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<ChannelData*> channel); not_null<PeerData*> peer);
Main::Session &session() const override; Main::Session &session() const override;
void prepare() override; void prepare() override;
@ -60,7 +61,7 @@ private:
struct SavedState : SavedStateBase { struct SavedState : SavedStateBase {
}; };
const not_null<Controller*> _controller; const not_null<Controller*> _controller;
const not_null<ChannelData*> _channel; const not_null<PeerData*> _peer;
Ui::RpWidget *_content = nullptr; Ui::RpWidget *_content = nullptr;
Ui::RpWidget *_unlock = nullptr; Ui::RpWidget *_unlock = nullptr;
rpl::variable<int> _unlockHeight; rpl::variable<int> _unlockHeight;
@ -69,14 +70,14 @@ private:
ListController::ListController( ListController::ListController(
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<ChannelData*> channel) not_null<PeerData*> peer)
: PeerListController() : PeerListController()
, _controller(controller) , _controller(controller)
, _channel(channel) { , _peer(peer) {
} }
Main::Session &ListController::session() const { Main::Session &ListController::session() const {
return _channel->session(); return _peer->session();
} }
std::unique_ptr<PeerListRow> ListController::createRow( std::unique_ptr<PeerListRow> ListController::createRow(
@ -95,20 +96,22 @@ std::unique_ptr<PeerListRow> ListController::createRow(
} }
void ListController::prepare() { void ListController::prepare() {
delegate()->peerListSetTitle(tr::lng_similar_channels_title()); delegate()->peerListSetTitle(_peer->isBroadcast()
? tr::lng_similar_channels_title()
: tr::lng_similar_bots_title());
const auto participants = &_channel->session().api().chatParticipants(); const auto participants = &_peer->session().api().chatParticipants();
Data::AmPremiumValue( Data::AmPremiumValue(
&_channel->session() &_peer->session()
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
participants->loadSimilarChannels(_channel); participants->loadSimilarPeers(_peer);
rebuild(); rebuild();
}, lifetime()); }, lifetime());
participants->similarLoaded( participants->similarLoaded(
) | rpl::filter( ) | rpl::filter(
rpl::mappers::_1 == _channel rpl::mappers::_1 == _peer
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
rebuild(); rebuild();
}, lifetime()); }, lifetime());
@ -123,16 +126,16 @@ rpl::producer<int> ListController::unlockHeightValue() const {
} }
void ListController::rebuild() { void ListController::rebuild() {
const auto participants = &_channel->session().api().chatParticipants(); const auto participants = &_peer->session().api().chatParticipants();
const auto &list = participants->similar(_channel); const auto &list = participants->similar(_peer);
for (const auto channel : list.list) { for (const auto peer : list.list) {
if (!delegate()->peerListFindRow(channel->id.value)) { if (!delegate()->peerListFindRow(peer->id.value)) {
delegate()->peerListAppendRow(createRow(channel)); delegate()->peerListAppendRow(createRow(peer));
} }
} }
if (!list.more if (!list.more
|| _channel->session().premium() || _peer->session().premium()
|| !_channel->session().premiumPossible()) { || !_peer->session().premiumPossible()) {
delete base::take(_unlock); delete base::take(_unlock);
_unlockHeight = 0; _unlockHeight = 0;
} else if (!_unlock) { } else if (!_unlock) {
@ -149,7 +152,9 @@ void ListController::setupUnlock() {
const auto button = ::Settings::CreateLockedButton( const auto button = ::Settings::CreateLockedButton(
_unlock, _unlock,
tr::lng_similar_channels_show_more(), (_peer->isBroadcast()
? tr::lng_similar_channels_show_more()
: tr::lng_similar_bots_show_more()),
st::similarChannelsLock, st::similarChannelsLock,
rpl::single(true)); rpl::single(true));
button->setClickedCallback([=] { button->setClickedCallback([=] {
@ -158,10 +163,12 @@ void ListController::setupUnlock() {
}); });
const auto upto = Data::PremiumLimits( const auto upto = Data::PremiumLimits(
&_channel->session()).similarChannelsPremium(); &_peer->session()).similarChannelsPremium();
const auto about = Ui::CreateChild<Ui::FlatLabel>( const auto about = Ui::CreateChild<Ui::FlatLabel>(
_unlock, _unlock,
tr::lng_similar_channels_premium_all( (_peer->isBroadcast()
? tr::lng_similar_channels_premium_all
: tr::lng_similar_bots_premium_all)(
lt_count, lt_count,
rpl::single(upto * 1.), rpl::single(upto * 1.),
lt_link, lt_link,
@ -177,7 +184,9 @@ void ListController::setupUnlock() {
rpl::combine( rpl::combine(
_content->sizeValue(), _content->sizeValue(),
tr::lng_similar_channels_show_more() (_peer->isBroadcast()
? tr::lng_similar_channels_show_more()
: tr::lng_similar_bots_show_more())
) | rpl::start_with_next([=](QSize size, const auto &) { ) | rpl::start_with_next([=](QSize size, const auto &) {
auto top = st::similarChannelsLockFade auto top = st::similarChannelsLockFade
+ st::similarChannelsLockPadding.top(); + st::similarChannelsLockPadding.top();
@ -263,10 +272,10 @@ public:
InnerWidget( InnerWidget(
QWidget *parent, QWidget *parent,
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<ChannelData*> channel); not_null<PeerData*> peer);
[[nodiscard]] not_null<ChannelData*> channel() const { [[nodiscard]] not_null<PeerData*> peer() const {
return _channel; return _peer;
} }
rpl::producer<Ui::ScrollToRequest> scrollToRequests() const; rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
@ -305,7 +314,7 @@ private:
const std::shared_ptr<Main::SessionShow> _show; const std::shared_ptr<Main::SessionShow> _show;
not_null<Controller*> _controller; not_null<Controller*> _controller;
const not_null<ChannelData*> _channel; const not_null<PeerData*> _peer;
std::unique_ptr<ListController> _listController; std::unique_ptr<ListController> _listController;
object_ptr<ListWidget> _list; object_ptr<ListWidget> _list;
@ -316,12 +325,12 @@ private:
InnerWidget::InnerWidget( InnerWidget::InnerWidget(
QWidget *parent, QWidget *parent,
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<ChannelData*> channel) not_null<PeerData*> peer)
: RpWidget(parent) : RpWidget(parent)
, _show(controller->uiShow()) , _show(controller->uiShow())
, _controller(controller) , _controller(controller)
, _channel(channel) , _peer(peer)
, _listController(std::make_unique<ListController>(controller, _channel)) , _listController(std::make_unique<ListController>(controller, _peer))
, _list(setupList(this, _listController.get())) { , _list(setupList(this, _listController.get())) {
setContent(_list.data()); setContent(_list.data());
_listController->setDelegate(static_cast<PeerListDelegate*>(this)); _listController->setDelegate(static_cast<PeerListDelegate*>(this));
@ -428,23 +437,19 @@ std::shared_ptr<Main::SessionShow> InnerWidget::peerListUiShow() {
return _show; return _show;
} }
Memento::Memento(not_null<ChannelData*> channel) Memento::Memento(not_null<PeerData*> peer)
: ContentMemento(channel, nullptr, PeerId()) { : ContentMemento(peer, nullptr, PeerId()) {
} }
Section Memento::section() const { Section Memento::section() const {
return Section(Section::Type::SimilarChannels); return Section(Section::Type::SimilarPeers);
}
not_null<ChannelData*> Memento::channel() const {
return peer()->asChannel();
} }
object_ptr<ContentWidget> Memento::createWidget( object_ptr<ContentWidget> Memento::createWidget(
QWidget *parent, QWidget *parent,
not_null<Controller*> controller, not_null<Controller*> controller,
const QRect &geometry) { const QRect &geometry) {
auto result = object_ptr<Widget>(parent, controller, channel()); auto result = object_ptr<Widget>(parent, controller, peer());
result->setInternalState(geometry, this); result->setInternalState(geometry, this);
return result; return result;
} }
@ -462,20 +467,22 @@ Memento::~Memento() = default;
Widget::Widget( Widget::Widget(
QWidget *parent, QWidget *parent,
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<ChannelData*> channel) not_null<PeerData*> peer)
: ContentWidget(parent, controller) { : ContentWidget(parent, controller) {
_inner = setInnerWidget(object_ptr<InnerWidget>( _inner = setInnerWidget(object_ptr<InnerWidget>(
this, this,
controller, controller,
channel)); peer));
} }
rpl::producer<QString> Widget::title() { rpl::producer<QString> Widget::title() {
return tr::lng_similar_channels_title(); return peer()->isBroadcast()
? tr::lng_similar_channels_title()
: tr::lng_similar_bots_title();
} }
not_null<ChannelData*> Widget::channel() const { not_null<PeerData*> Widget::peer() const {
return _inner->channel(); return _inner->peer();
} }
bool Widget::showInternal(not_null<ContentMemento*> memento) { bool Widget::showInternal(not_null<ContentMemento*> memento) {
@ -483,7 +490,7 @@ bool Widget::showInternal(not_null<ContentMemento*> memento) {
return false; return false;
} }
if (auto similarMemento = dynamic_cast<Memento*>(memento.get())) { if (auto similarMemento = dynamic_cast<Memento*>(memento.get())) {
if (similarMemento->channel() == channel()) { if (similarMemento->peer() == peer()) {
restoreState(similarMemento); restoreState(similarMemento);
return true; return true;
} }
@ -500,7 +507,7 @@ void Widget::setInternalState(
} }
std::shared_ptr<ContentMemento> Widget::doCreateMemento() { std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
auto result = std::make_shared<Memento>(channel()); auto result = std::make_shared<Memento>(peer());
saveState(result.get()); saveState(result.get());
return result; return result;
} }
@ -515,4 +522,4 @@ void Widget::restoreState(not_null<Memento*> memento) {
scrollTopRestore(memento->scrollTop()); scrollTopRestore(memento->scrollTop());
} }
} // namespace Info::SimilarChannels } // namespace Info::SimilarPeers

View file

@ -9,16 +9,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_content_widget.h" #include "info/info_content_widget.h"
class ChannelData; class PeerData;
struct PeerListState; struct PeerListState;
namespace Info::SimilarChannels { namespace Info::SimilarPeers {
class InnerWidget; class InnerWidget;
class Memento final : public ContentMemento { class Memento final : public ContentMemento {
public: public:
explicit Memento(not_null<ChannelData*> channel); explicit Memento(not_null<PeerData*> peer);
object_ptr<ContentWidget> createWidget( object_ptr<ContentWidget> createWidget(
QWidget *parent, QWidget *parent,
@ -27,8 +27,6 @@ public:
Section section() const override; Section section() const override;
[[nodiscard]] not_null<ChannelData*> channel() const;
void setListState(std::unique_ptr<PeerListState> state); void setListState(std::unique_ptr<PeerListState> state);
std::unique_ptr<PeerListState> listState(); std::unique_ptr<PeerListState> listState();
@ -44,9 +42,9 @@ public:
Widget( Widget(
QWidget *parent, QWidget *parent,
not_null<Controller*> controller, not_null<Controller*> controller,
not_null<ChannelData*> channel); not_null<PeerData*> peer);
[[nodiscard]] not_null<ChannelData*> channel() const; [[nodiscard]] not_null<PeerData*> peer() const;
bool showInternal( bool showInternal(
not_null<ContentMemento*> memento) override; not_null<ContentMemento*> memento) override;
@ -67,4 +65,4 @@ private:
}; };
} // namespace Info::SimilarChannels } // namespace Info::SimilarPeers