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) { void Story::setReactionId(Data::ReactionId id) {
if (_sentReactionId != id) { if (_sentReactionId != id) {
const auto wasEmpty = _sentReactionId.empty(); const auto wasEmpty = _sentReactionId.empty();
changeSuggestedReactionCount(_sentReactionId, -1);
_sentReactionId = id; _sentReactionId = id;
auto flags = UpdateFlag::Reaction | UpdateFlag(); changeSuggestedReactionCount(id, 1);
if (_views.known && _sentReactionId.empty() != wasEmpty) { if (_views.known && _sentReactionId.empty() != wasEmpty) {
const auto delta = wasEmpty ? 1 : -1; const auto delta = wasEmpty ? 1 : -1;
if (_views.reactions + delta >= 0) { if (_views.reactions + delta >= 0) {
_views.reactions += delta; _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 reactions = _views.reactions;
auto viewers = std::vector<not_null<PeerData*>>(); auto viewers = std::vector<not_null<PeerData*>>();
auto viewsKnown = _views.known; auto viewsKnown = _views.known;
auto reactionsCounts = base::flat_map<Data::ReactionId, int>();
if (const auto info = data.vviews()) { if (const auto info = data.vviews()) {
views = info->data().vviews_count().v; const auto &data = info->data();
reactions = info->data().vreactions_count().value_or_empty(); views = data.vviews_count().v;
reactions = data.vreactions_count().value_or_empty();
viewsKnown = true; viewsKnown = true;
if (const auto list = info->data().vrecent_viewers()) { if (const auto list = data.vrecent_viewers()) {
viewers.reserve(list->v.size()); viewers.reserve(list->v.size());
auto &owner = _peer->owner(); auto &owner = _peer->owner();
auto &&cut = list->v auto &&cut = list->v
@ -552,8 +566,33 @@ void Story::applyFields(
viewers.push_back(owner.peer(peerFromUser(id))); 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 { } else {
viewers = _recentViewers; viewers = _recentViewers;
for (const auto &suggested : _suggestedReactions) {
if (const auto count = suggested.count) {
reactionsCounts[suggested.reaction] = count;
}
}
} }
auto locations = std::vector<StoryLocation>(); auto locations = std::vector<StoryLocation>();
auto suggestedReactions = std::vector<SuggestedReaction>(); auto suggestedReactions = std::vector<SuggestedReaction>();
@ -563,7 +602,11 @@ void Story::applyFields(
for (const auto &area : areas->v) { for (const auto &area : areas->v) {
if (const auto location = ParseLocation(area)) { if (const auto location = ParseLocation(area)) {
locations.push_back(*location); 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); suggestedReactions.push_back(*reaction);
} }
} }

View file

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

View file

@ -892,6 +892,9 @@ bool Controller::changeShown(Data::Story *story) {
const auto id = story ? story->fullId() : FullStoryId(); const auto id = story ? story->fullId() : FullStoryId();
const auto session = story ? &story->session() : nullptr; const auto session = story ? &story->session() : nullptr;
const auto sessionChanged = (_session != session); const auto sessionChanged = (_session != session);
updateAreas(story);
if (_shown == id && !sessionChanged) { if (_shown == id && !sessionChanged) {
return false; return false;
} }
@ -915,21 +918,6 @@ bool Controller::changeShown(Data::Story *story) {
Data::Stories::Polling::Viewer); 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; _viewed = false;
invalidate_weak_ptrs(&_viewsLoadGuard); invalidate_weak_ptrs(&_viewsLoadGuard);
_reactions->hide(); _reactions->hide();
@ -963,6 +951,7 @@ void Controller::subscribeToSession() {
_session->changes().storyUpdates( _session->changes().storyUpdates(
Data::StoryUpdate::Flag::Edited Data::StoryUpdate::Flag::Edited
| Data::StoryUpdate::Flag::ViewsChanged | Data::StoryUpdate::Flag::ViewsChanged
| Data::StoryUpdate::Flag::Reaction
) | rpl::filter([=](const Data::StoryUpdate &update) { ) | rpl::filter([=](const Data::StoryUpdate &update) {
return (update.story == this->story()); return (update.story == this->story());
}) | rpl::start_with_next([=](const Data::StoryUpdate &update) { }) | rpl::start_with_next([=](const Data::StoryUpdate &update) {
@ -977,6 +966,7 @@ void Controller::subscribeToSession() {
.self = update.story->peer()->isSelf(), .self = update.story->peer()->isSelf(),
.channel = update.story->peer()->isChannel(), .channel = update.story->peer()->isChannel(),
}); });
updateAreas(update.story);
} }
}, _sessionLifetime); }, _sessionLifetime);
_sessionLifetime.add([=] { _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 { PauseState Controller::pauseState() const {
const auto inactive = !_windowActive const auto inactive = !_windowActive
|| _replyActive || _replyActive

View file

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

View file

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

View file

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

View file

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

View file

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