Added list of public forwards for story statistics.

This commit is contained in:
23rd 2023-11-28 05:30:16 +03:00 committed by John Preston
parent dfe55b26a2
commit 2542ec5d93
8 changed files with 157 additions and 56 deletions

View file

@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_peer.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "history/history.h" #include "history/history.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -352,7 +351,7 @@ Data::SupergroupStatistics Statistics::supergroupStats() const {
PublicForwards::PublicForwards( PublicForwards::PublicForwards(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
FullMsgId fullId) Data::RecentPostId fullId)
: StatisticsRequestSender(channel) : StatisticsRequestSender(channel)
, _fullId(fullId) { , _fullId(fullId) {
} }
@ -360,9 +359,20 @@ PublicForwards::PublicForwards(
void PublicForwards::request( void PublicForwards::request(
const Data::PublicForwardsSlice::OffsetToken &token, const Data::PublicForwardsSlice::OffsetToken &token,
Fn<void(Data::PublicForwardsSlice)> done) { Fn<void(Data::PublicForwardsSlice)> done) {
if (_requestId) { if (!_requestId) {
return; if (_fullId.messageId) {
requestMessage(token, std::move(done));
} else if (_fullId.storyId) {
requestStory(token, std::move(done));
}
} }
}
void PublicForwards::requestMessage(
const Data::PublicForwardsSlice::OffsetToken &token,
Fn<void(Data::PublicForwardsSlice)> done) {
Expects(_fullId.messageId);
const auto offsetPeer = channel()->owner().peer(token.fullId.peer); const auto offsetPeer = channel()->owner().peer(token.fullId.peer);
const auto tlOffsetPeer = offsetPeer const auto tlOffsetPeer = offsetPeer
? offsetPeer->input ? offsetPeer->input
@ -370,13 +380,13 @@ void PublicForwards::request(
constexpr auto kLimit = tl::make_int(100); constexpr auto kLimit = tl::make_int(100);
_requestId = makeRequest(MTPstats_GetMessagePublicForwards( _requestId = makeRequest(MTPstats_GetMessagePublicForwards(
channel()->inputChannel, channel()->inputChannel,
MTP_int(_fullId.msg), MTP_int(_fullId.messageId.msg),
MTP_int(token.rate), MTP_int(token.rate),
tlOffsetPeer, tlOffsetPeer,
MTP_int(token.fullId.msg), MTP_int(token.fullId.msg),
kLimit kLimit
)).done([=, channel = channel()](const MTPmessages_Messages &result) { )).done([=, channel = channel()](const MTPmessages_Messages &result) {
using Messages = QVector<FullMsgId>; using Messages = QVector<Data::RecentPostId>;
_requestId = 0; _requestId = 0;
auto nextToken = Data::PublicForwardsSlice::OffsetToken(); auto nextToken = Data::PublicForwardsSlice::OffsetToken();
@ -393,7 +403,7 @@ void PublicForwards::request(
MessageFlags(), MessageFlags(),
NewMessageType::Existing); NewMessageType::Existing);
nextToken.fullId = { peerId, msgId }; nextToken.fullId = { peerId, msgId };
result.push_back(nextToken.fullId); result.push_back({ .messageId = nextToken.fullId });
} }
} }
} }
@ -448,11 +458,79 @@ void PublicForwards::request(
}).send(); }).send();
} }
void PublicForwards::requestStory(
const Data::PublicForwardsSlice::OffsetToken &token,
Fn<void(Data::PublicForwardsSlice)> done) {
Expects(_fullId.storyId);
constexpr auto kLimit = tl::make_int(100);
_requestId = makeRequest(MTPstats_GetStoryPublicForwards(
channel()->input,
MTP_int(_fullId.storyId.story),
MTP_string(token.storyOffset),
kLimit
)).done([=, channel = channel()](
const MTPstats_PublicForwards &tlForwards) {
using Messages = QVector<Data::RecentPostId>;
_requestId = 0;
const auto &data = tlForwards.data();
channel->owner().processUsers(data.vusers());
channel->owner().processChats(data.vchats());
const auto nextToken = Data::PublicForwardsSlice::OffsetToken({
.storyOffset = data.vnext_offset().value_or_empty(),
});
const auto allLoaded = nextToken.storyOffset.isEmpty()
|| (nextToken.storyOffset == token.storyOffset);
const auto fullCount = data.vcount().v;
auto recentList = Messages();
for (const auto &tlForward : data.vforwards().v) {
tlForward.match([&](const MTPDpublicForwardMessage &data) {
const auto &message = data.vmessage();
const auto msgId = IdFromMessage(message);
const auto peerId = PeerFromMessage(message);
const auto lastDate = DateFromMessage(message);
if (const auto peer = channel->owner().peerLoaded(peerId)) {
if (!lastDate) {
return;
}
channel->owner().addNewMessage(
message,
MessageFlags(),
NewMessageType::Existing);
recentList.push_back({ .messageId = { peerId, msgId } });
}
}, [&](const MTPDpublicForwardStory &data) {
data.vstory().match([&](const MTPDstoryItem &d) {
recentList.push_back({
.storyId = { peerFromMTP(data.vpeer()), d.vid().v }
});
}, [](const auto &) {
});
});
}
_lastTotal = std::max(_lastTotal, fullCount);
done({
.list = std::move(recentList),
.total = _lastTotal,
.allLoaded = allLoaded,
.token = nextToken,
});
}).fail([=] {
_requestId = 0;
}).send();
}
MessageStatistics::MessageStatistics( MessageStatistics::MessageStatistics(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
FullMsgId fullId) FullMsgId fullId)
: StatisticsRequestSender(channel) : StatisticsRequestSender(channel)
, _publicForwards(channel, fullId) , _publicForwards(channel, { .messageId = fullId })
, _fullId(fullId) { , _fullId(fullId) {
} }
@ -460,7 +538,7 @@ MessageStatistics::MessageStatistics(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
FullStoryId storyId) FullStoryId storyId)
: StatisticsRequestSender(channel) : StatisticsRequestSender(channel)
, _publicForwards(channel, {}) , _publicForwards(channel, { .storyId = storyId })
, _storyId(storyId) { , _storyId(storyId) {
} }
@ -476,7 +554,7 @@ void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
const Data::StatisticalGraph &messageGraph, const Data::StatisticalGraph &messageGraph,
const Data::StatisticalGraph &reactionsGraph, const Data::StatisticalGraph &reactionsGraph,
const Data::StatisticsMessageInteractionInfo &info) { const Data::StatisticsMessageInteractionInfo &info) {
_publicForwards.request({}, [=](Data::PublicForwardsSlice slice) { const auto callback = [=](Data::PublicForwardsSlice slice) {
const auto total = slice.total; const auto total = slice.total;
_firstSlice = std::move(slice); _firstSlice = std::move(slice);
done({ done({
@ -487,7 +565,8 @@ void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
.views = info.viewsCount, .views = info.viewsCount,
.reactions = info.reactionsCount, .reactions = info.reactionsCount,
}); });
}); };
_publicForwards.request({}, callback);
}; };
const auto requestPrivateForwards = [=]( const auto requestPrivateForwards = [=](
@ -553,22 +632,27 @@ void MessageStatistics::request(Fn<void(Data::MessageStatistics)> done) {
MTP_vector<MTPint>(1, MTP_int(_storyId.story))) MTP_vector<MTPint>(1, MTP_int(_storyId.story)))
).done([=](const MTPstories_Stories &result) { ).done([=](const MTPstories_Stories &result) {
const auto &storyItem = result.data().vstories().v.front(); const auto &storyItem = result.data().vstories().v.front();
storyItem.match([&](const MTPDstoryItem &data) { auto info = storyItem.match([&](const MTPDstoryItem &data) {
if (!data.vviews()) { if (!data.vviews()) {
return; return Data::StatisticsMessageInteractionInfo();
} }
const auto &tlViews = data.vviews()->data(); const auto &tlViews = data.vviews()->data();
done({ return Data::StatisticsMessageInteractionInfo{
.messageInteractionGraph = messageGraph, .storyId = data.vid().v,
.reactionsByEmotionGraph = reactionsGraph, .viewsCount = tlViews.vviews_count().v,
.publicForwards = -1, .forwardsCount = tlViews.vforwards_count().value_or(0),
.privateForwards = tlViews.vforwards_count().value_or(0), .reactionsCount = tlViews.vreactions_count().value_or(0),
.views = tlViews.vviews_count().v, };
.reactions = tlViews.vreactions_count().value_or(0),
});
}, [](const auto &) { }, [](const auto &) {
return Data::StatisticsMessageInteractionInfo();
}); });
requestFirstPublicForwards(
messageGraph,
reactionsGraph,
std::move(info));
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
requestFirstPublicForwards(messageGraph, reactionsGraph, {});
}).send(); }).send();
}; };

View file

@ -68,14 +68,23 @@ private:
class PublicForwards final : public StatisticsRequestSender { class PublicForwards final : public StatisticsRequestSender {
public: public:
PublicForwards(not_null<ChannelData*> channel, FullMsgId fullId); PublicForwards(
not_null<ChannelData*> channel,
Data::RecentPostId fullId);
void request( void request(
const Data::PublicForwardsSlice::OffsetToken &token, const Data::PublicForwardsSlice::OffsetToken &token,
Fn<void(Data::PublicForwardsSlice)> done); Fn<void(Data::PublicForwardsSlice)> done);
private: private:
const FullMsgId _fullId; void requestMessage(
const Data::PublicForwardsSlice::OffsetToken &token,
Fn<void(Data::PublicForwardsSlice)> done);
void requestStory(
const Data::PublicForwardsSlice::OffsetToken &token,
Fn<void(Data::PublicForwardsSlice)> done);
const Data::RecentPostId _fullId;
mtpRequestId _requestId = 0; mtpRequestId _requestId = 0;
int _lastTotal = 0; int _lastTotal = 0;

View file

@ -133,12 +133,27 @@ struct AnyStatistics final {
Data::StoryStatistics story; Data::StoryStatistics story;
}; };
struct RecentPostId final {
FullMsgId messageId;
FullStoryId storyId;
[[nodiscard]] bool valid() const {
return messageId || storyId;
}
explicit operator bool() const {
return valid();
}
friend inline auto operator<=>(RecentPostId, RecentPostId) = default;
friend inline bool operator==(RecentPostId, RecentPostId) = default;
};
struct PublicForwardsSlice final { struct PublicForwardsSlice final {
struct OffsetToken final { struct OffsetToken final {
int rate = 0; int rate = 0;
FullMsgId fullId; FullMsgId fullId;
QString storyOffset;
}; };
QVector<FullMsgId> list; QVector<RecentPostId> list;
int total = 0; int total = 0;
bool allLoaded = false; bool allLoaded = false;
OffsetToken token; OffsetToken token;

View file

@ -11,23 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Info::Statistics { namespace Info::Statistics {
struct RecentPostId final {
FullMsgId messageId;
FullStoryId storyId;
[[nodiscard]] bool valid() const {
return messageId || storyId;
}
explicit operator bool() const {
return valid();
}
friend inline auto operator<=>(RecentPostId, RecentPostId) = default;
friend inline bool operator==(RecentPostId, RecentPostId) = default;
};
struct SavedState final { struct SavedState final {
Data::AnyStatistics stats; Data::AnyStatistics stats;
base::flat_map<RecentPostId, QImage> recentPostPreviews; base::flat_map<Data::RecentPostId, QImage> recentPostPreviews;
Data::PublicForwardsSlice publicForwardsFirstSlice; Data::PublicForwardsSlice publicForwardsFirstSlice;
int recentPostsExpanded = 0; int recentPostsExpanded = 0;
}; };

View file

@ -641,7 +641,7 @@ void InnerWidget::load() {
.message = _contextId ? data : Data::StoryStatistics(), .message = _contextId ? data : Data::StoryStatistics(),
.story = _storyId ? data : Data::StoryStatistics(), .story = _storyId ? data : Data::StoryStatistics(),
}; };
if (_contextId) { if (_contextId || _storyId) {
_state.publicForwardsFirstSlice = api->firstSlice(); _state.publicForwardsFirstSlice = api->firstSlice();
} }
fill(); fill();
@ -726,13 +726,14 @@ void InnerWidget::fill() {
descriptor.peer, descriptor.peer,
tr::lng_stats_inviters_title()); tr::lng_stats_inviters_title());
} }
} else if (message) { } else if (_state.stats.message || _state.stats.story) {
using namespace Data;
AddPublicForwards( AddPublicForwards(
_state.publicForwardsFirstSlice, _state.publicForwardsFirstSlice,
inner, inner,
[=](FullMsgId id) { _showRequests.fire({ .history = id }); }, [=](FullMsgId id) { _showRequests.fire({ .history = id }); },
descriptor.peer, descriptor.peer,
_contextId); RecentPostId{ .messageId = _contextId, .storyId = _storyId });
} }
} }
@ -762,7 +763,7 @@ void InnerWidget::fillRecentPosts() {
messageWrap, messageWrap,
rpl::never<QString>(), rpl::never<QString>(),
st::statisticsRecentPostButton)); st::statisticsRecentPostButton));
const auto fullRecentId = RecentPostId{ const auto fullRecentId = Data::RecentPostId{
.messageId = maybeItem ? maybeItem->fullId() : FullMsgId(), .messageId = maybeItem ? maybeItem->fullId() : FullMsgId(),
.storyId = maybeStory ? maybeStory->fullId() : FullStoryId(), .storyId = maybeStory ? maybeStory->fullId() : FullStoryId(),
}; };
@ -886,7 +887,8 @@ void InnerWidget::restoreState(not_null<Memento*> memento) {
_state = memento->state(); _state = memento->state();
if (_state.stats.channel if (_state.stats.channel
|| _state.stats.supergroup || _state.stats.supergroup
|| _state.stats.message) { || _state.stats.message
|| _state.stats.story) {
fill(); fill();
} else { } else {
load(); load();

View file

@ -91,11 +91,11 @@ void AddSubtitle(
return resultText; return resultText;
} }
struct Descriptor final { struct PublicForwardsDescriptor final {
Data::PublicForwardsSlice firstSlice; Data::PublicForwardsSlice firstSlice;
Fn<void(FullMsgId)> showPeerHistory; Fn<void(FullMsgId)> showPeerHistory;
not_null<PeerData*> peer; not_null<PeerData*> peer;
FullMsgId contextId; Data::RecentPostId contextId;
}; };
struct MembersDescriptor final { struct MembersDescriptor final {
@ -229,7 +229,7 @@ void MembersController::rowClicked(not_null<PeerListRow*> row) {
class PublicForwardsController final : public PeerListController { class PublicForwardsController final : public PeerListController {
public: public:
explicit PublicForwardsController(Descriptor d); explicit PublicForwardsController(PublicForwardsDescriptor d);
Main::Session &session() const override; Main::Session &session() const override;
void prepare() override; void prepare() override;
@ -251,7 +251,7 @@ private:
}; };
PublicForwardsController::PublicForwardsController(Descriptor d) PublicForwardsController::PublicForwardsController(PublicForwardsDescriptor d)
: _session(&d.peer->session()) : _session(&d.peer->session())
, _showPeerHistory(std::move(d.showPeerHistory)) , _showPeerHistory(std::move(d.showPeerHistory))
, _api(d.peer->asChannel(), d.contextId) , _api(d.peer->asChannel(), d.contextId)
@ -282,8 +282,11 @@ void PublicForwardsController::applySlice(
_apiToken = slice.token; _apiToken = slice.token;
for (const auto &item : slice.list) { for (const auto &item : slice.list) {
if (const auto peer = session().data().peerLoaded(item.peer)) { // TODO support stories.
appendRow(peer, item.msg); if (const auto fullId = item.messageId) {
if (const auto peer = session().data().peerLoaded(fullId.peer)) {
appendRow(peer, fullId.msg);
}
} }
} }
delegate()->peerListRefreshRows(); delegate()->peerListRefreshRows();
@ -617,23 +620,24 @@ void AddPublicForwards(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
Fn<void(FullMsgId)> showPeerHistory, Fn<void(FullMsgId)> showPeerHistory,
not_null<PeerData*> peer, not_null<PeerData*> peer,
FullMsgId contextId) { Data::RecentPostId contextId) {
if (!peer->isChannel()) { if (!peer->isChannel()) {
return; return;
} }
struct State final { struct State final {
State(Descriptor d) : controller(std::move(d)) { State(PublicForwardsDescriptor d) : controller(std::move(d)) {
} }
PeerListContentDelegateSimple delegate; PeerListContentDelegateSimple delegate;
PublicForwardsController controller; PublicForwardsController controller;
}; };
const auto state = container->lifetime().make_state<State>(Descriptor{ auto d = PublicForwardsDescriptor{
firstSlice, firstSlice,
std::move(showPeerHistory), std::move(showPeerHistory),
peer, peer,
contextId, contextId,
}); };
const auto state = container->lifetime().make_state<State>(std::move(d));
if (const auto total = firstSlice.total; total > 0) { if (const auto total = firstSlice.total; total > 0) {
AddSubtitle( AddSubtitle(

View file

@ -17,6 +17,7 @@ namespace Data {
struct Boost; struct Boost;
struct BoostsListSlice; struct BoostsListSlice;
struct PublicForwardsSlice; struct PublicForwardsSlice;
struct RecentPostId;
struct SupergroupStatistics; struct SupergroupStatistics;
} // namespace Data } // namespace Data
@ -27,7 +28,7 @@ void AddPublicForwards(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
Fn<void(FullMsgId)> showPeerHistory, Fn<void(FullMsgId)> showPeerHistory,
not_null<PeerData*> peer, not_null<PeerData*> peer,
FullMsgId contextId); Data::RecentPostId contextId);
void AddMembersList( void AddMembersList(
Data::SupergroupStatistics data, Data::SupergroupStatistics data,

View file

@ -349,7 +349,7 @@ void MessagePreview::paintEvent(QPaintEvent *e) {
void MessagePreview::saveState(SavedState &state) const { void MessagePreview::saveState(SavedState &state) const {
if (!_lifetimeDownload) { if (!_lifetimeDownload) {
const auto fullId = RecentPostId{ _messageId, _storyId }; const auto fullId = Data::RecentPostId{ _messageId, _storyId };
state.recentPostPreviews[fullId] = _preview; state.recentPostPreviews[fullId] = _preview;
} }
} }