Implement suggested reaction count.

This commit is contained in:
John Preston 2023-09-14 12:29:14 +04:00
parent f3db7e636b
commit 5d5cae7860
8 changed files with 150 additions and 27 deletions

View File

@ -411,16 +411,28 @@ Data::ReactionId Story::sentReactionId() const {
void Story::setReactionId(Data::ReactionId id) {
if (_sentReactionId != id) {
const auto wasEmpty = _sentReactionId.empty();
changeSuggestedReactionCount(_sentReactionId, -1);
_sentReactionId = id;
auto flags = UpdateFlag::Reaction | UpdateFlag();
changeSuggestedReactionCount(id, 1);
if (_views.known && _sentReactionId.empty() != wasEmpty) {
const auto delta = wasEmpty ? 1 : -1;
if (_views.reactions + delta >= 0) {
_views.reactions += delta;
flags |= UpdateFlag::ViewsChanged;
}
}
session().changes().storyUpdated(this, flags);
session().changes().storyUpdated(this, UpdateFlag::Reaction);
}
}
void Story::changeSuggestedReactionCount(Data::ReactionId id, int delta) {
if (id.empty() || !_peer->isChannel()) {
return;
}
for (auto &suggested : _suggestedReactions) {
if (suggested.reaction == id && suggested.count + delta >= 0) {
suggested.count += delta;
}
}
}
@ -539,11 +551,13 @@ void Story::applyFields(
auto reactions = _views.reactions;
auto viewers = std::vector<not_null<PeerData*>>();
auto viewsKnown = _views.known;
auto reactionsCounts = base::flat_map<Data::ReactionId, int>();
if (const auto info = data.vviews()) {
views = info->data().vviews_count().v;
reactions = info->data().vreactions_count().value_or_empty();
const auto &data = info->data();
views = data.vviews_count().v;
reactions = data.vreactions_count().value_or_empty();
viewsKnown = true;
if (const auto list = info->data().vrecent_viewers()) {
if (const auto list = data.vrecent_viewers()) {
viewers.reserve(list->v.size());
auto &owner = _peer->owner();
auto &&cut = list->v
@ -552,8 +566,33 @@ void Story::applyFields(
viewers.push_back(owner.peer(peerFromUser(id)));
}
}
auto total = 0;
if (const auto list = data.vreactions()) {
reactionsCounts.reserve(list->v.size());
for (const auto &reaction : list->v) {
const auto &data = reaction.data();
const auto id = Data::ReactionFromMTP(data.vreaction());
const auto count = data.vcount().v;
reactionsCounts[id] = count;
total += count;
}
}
if (!reaction.empty()) {
if (auto &mine = reactionsCounts[reaction]; !mine) {
mine = 1;
++total;
}
}
if (reactions < total) {
reactions = total;
}
} else {
viewers = _recentViewers;
for (const auto &suggested : _suggestedReactions) {
if (const auto count = suggested.count) {
reactionsCounts[suggested.reaction] = count;
}
}
}
auto locations = std::vector<StoryLocation>();
auto suggestedReactions = std::vector<SuggestedReaction>();
@ -563,7 +602,11 @@ void Story::applyFields(
for (const auto &area : areas->v) {
if (const auto location = ParseLocation(area)) {
locations.push_back(*location);
} else if (const auto reaction = ParseSuggestedReaction(area)) {
} else if (auto reaction = ParseSuggestedReaction(area)) {
const auto i = reactionsCounts.find(reaction->reaction);
if (i != end(reactionsCounts)) {
reaction->count = i->second;
}
suggestedReactions.push_back(*reaction);
}
}

View File

@ -100,6 +100,7 @@ struct StoryLocation {
struct SuggestedReaction {
StoryArea area;
Data::ReactionId reaction;
int count = 0;
bool flipped = false;
bool dark = false;
@ -180,6 +181,7 @@ public:
[[nodiscard]] TimeId lastUpdateTime() const;
private:
void changeSuggestedReactionCount(Data::ReactionId id, int delta);
void applyFields(
StoryMedia media,
const MTPDstoryItem &data,

View File

@ -892,6 +892,9 @@ bool Controller::changeShown(Data::Story *story) {
const auto id = story ? story->fullId() : FullStoryId();
const auto session = story ? &story->session() : nullptr;
const auto sessionChanged = (_session != session);
updateAreas(story);
if (_shown == id && !sessionChanged) {
return false;
}
@ -915,21 +918,6 @@ bool Controller::changeShown(Data::Story *story) {
Data::Stories::Polling::Viewer);
}
const auto &locations = story
? story->locations()
: std::vector<Data::StoryLocation>();
const auto &suggestedReactions = story
? story->suggestedReactions()
: std::vector<Data::SuggestedReaction>();
if (_locations != locations) {
_locations = locations;
_areas.clear();
}
if (_suggestedReactions != suggestedReactions) {
_suggestedReactions = suggestedReactions;
_areas.clear();
}
_viewed = false;
invalidate_weak_ptrs(&_viewsLoadGuard);
_reactions->hide();
@ -963,6 +951,7 @@ void Controller::subscribeToSession() {
_session->changes().storyUpdates(
Data::StoryUpdate::Flag::Edited
| Data::StoryUpdate::Flag::ViewsChanged
| Data::StoryUpdate::Flag::Reaction
) | rpl::filter([=](const Data::StoryUpdate &update) {
return (update.story == this->story());
}) | rpl::start_with_next([=](const Data::StoryUpdate &update) {
@ -977,6 +966,7 @@ void Controller::subscribeToSession() {
.self = update.story->peer()->isSelf(),
.channel = update.story->peer()->isChannel(),
});
updateAreas(update.story);
}
}, _sessionLifetime);
_sessionLifetime.add([=] {
@ -984,6 +974,41 @@ void Controller::subscribeToSession() {
});
}
void Controller::updateAreas(Data::Story *story) {
const auto &locations = story
? story->locations()
: std::vector<Data::StoryLocation>();
const auto &suggestedReactions = story
? story->suggestedReactions()
: std::vector<Data::SuggestedReaction>();
if (_locations != locations) {
_locations = locations;
_areas.clear();
}
const auto reactionsCount = int(suggestedReactions.size());
if (_suggestedReactions.size() == reactionsCount && !_areas.empty()) {
for (auto i = 0; i != reactionsCount; ++i) {
const auto count = suggestedReactions[i].count;
if (_suggestedReactions[i].count != count) {
_suggestedReactions[i].count = count;
_areas[i + _locations.size()].reaction->updateCount(count);
}
if (_suggestedReactions[i] != suggestedReactions[i]) {
_suggestedReactions = suggestedReactions;
_areas.clear();
break;
}
}
} else if (_suggestedReactions != suggestedReactions) {
_suggestedReactions = suggestedReactions;
_areas.clear();
}
if (_areas.empty() || _suggestedReactions.empty()) {
return;
}
}
PauseState Controller::pauseState() const {
const auto inactive = !_windowActive
|| _replyActive

View File

@ -247,6 +247,7 @@ private:
const std::vector<Data::StoriesSourceInfo> &lists,
int index);
void updateAreas(Data::Story *story);
void reactionChosen(ReactionsMode mode, ChosenReaction chosen);
const not_null<Delegate*> _delegate;

View File

@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "main/main_session.h"
#include "media/stories/media_stories_controller.h"
#include "lang/lang_tag.h"
#include "ui/chat/chat_style.h"
#include "ui/effects/emoji_fly_animation.h"
#include "ui/effects/path_shift_gradient.h"
@ -55,6 +56,7 @@ constexpr auto kSuggestedTailSmallOffset = 0.697;
constexpr auto kSuggestedTailBigRotation = -42.29;
constexpr auto kSuggestedTailSmallRotation = -40.87;
constexpr auto kSuggestedReactionSize = 0.7;
constexpr auto kSuggestedWithCountSize = 0.55;
class ReactionView final
: public Ui::RpWidget
@ -67,6 +69,7 @@ public:
const Data::SuggestedReaction &reaction);
void setAreaGeometry(QRect geometry) override;
void updateCount(int count) override;
private:
using Element = HistoryView::Element;
@ -85,6 +88,8 @@ private:
std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
AdminLog::OwnedItem _fake;
QImage _background;
QString _countShort;
Ui::Text::String _counter;
QRectF _bubbleGeometry;
int _size = 0;
int _mediaLeft = 0;
@ -183,6 +188,9 @@ ReactionView::ReactionView(
}
}, lifetime());
_data.count = 0;
updateCount(reaction.count);
setAttribute(Qt::WA_TransparentForMouseEvents);
show();
}
@ -202,6 +210,24 @@ void ReactionView::setAreaGeometry(QRect geometry) {
setGeometry(geometry.marginsAdded({ add, add, add, add }));
}
void ReactionView::updateCount(int count) {
if (_data.count == count) {
return;
}
_data.count = count;
const auto countShort = Lang::FormatCountToShort(count).string;
if (_countShort == countShort) {
return;
}
_countShort = countShort;
if (!count) {
_counter = {};
} else {
_counter = { st::storiesLikeCountStyle, _countShort };
}
update();
}
not_null<HistoryView::ElementDelegate*> ReactionView::delegate() {
return static_cast<HistoryView::ElementDelegate*>(this);
}
@ -215,15 +241,34 @@ void ReactionView::paintEvent(QPaintEvent *e) {
}
p.drawImage(0, 0, _background);
const auto counter = !_counter.isEmpty();
const auto scale = counter
? kSuggestedWithCountSize
: kSuggestedReactionSize;
const auto counterSkip = counter
? ((kSuggestedReactionSize - kSuggestedWithCountSize)
* _mediaHeight / 2)
: 0;
auto hq = PainterHighQualityEnabler(p);
p.translate(_bubbleGeometry.center());
p.scale(
kSuggestedReactionSize * _bubbleGeometry.width() / _mediaWidth,
kSuggestedReactionSize * _bubbleGeometry.height() / _mediaHeight);
scale * _bubbleGeometry.width() / _mediaWidth,
scale * _bubbleGeometry.height() / _mediaHeight);
p.rotate(_data.area.rotation);
p.translate(
-(_mediaLeft + (_mediaWidth / 2)),
-(_mediaTop + (_mediaHeight / 2)));
-(_mediaTop + (_mediaHeight / 2) + counterSkip));
if (counter) {
p.setPen(_data.dark ? Qt::white : Qt::black);
_counter.draw(
p,
_mediaLeft,
_mediaTop + _mediaHeight,
_mediaWidth,
style::al_top);
}
auto context = Ui::ChatPaintContext{
.st = _chatStyle.get(),

View File

@ -46,6 +46,7 @@ public:
virtual ~SuggestedReactionView() = default;
virtual void setAreaGeometry(QRect geometry) = 0;
virtual void updateCount(int count) = 0;
};
class Reactions final {

View File

@ -323,7 +323,7 @@ void RecentViews::setupViewsReactions() {
) | rpl::start_with_next([=](int width) {
width += width
? st::storiesLikesTextRightSkip
: st::storiesLieksEmptyRightSkip;
: st::storiesLikesEmptyRightSkip;
_likeWrap->resize(likes->x() + width, _likeIcon->height());
updateViewsReactionsGeometry();
}, _likeWrap->lifetime());

View File

@ -1007,4 +1007,10 @@ storiesLikesText: FlatLabel(defaultFlatLabel) {
}
storiesLikesTextPosition: point(41px, 14px);
storiesLikesTextRightSkip: 8px;
storiesLieksEmptyRightSkip: 2px;
storiesLikesEmptyRightSkip: 2px;
storiesLikeCountStyle: TextStyle(defaultTextStyle) {
font: font(32px semibold);
linkFont: font(32px semibold);
linkFontOver: font(32px semibold underline);
}