Animate reactions when reading.

This commit is contained in:
John Preston 2022-01-28 14:44:33 +03:00
parent e509da8fd8
commit 4f1e04cf9e
17 changed files with 85 additions and 64 deletions

View file

@ -1294,7 +1294,7 @@ void ApiWrap::markContentsRead(
QVector<MTPint>>(); QVector<MTPint>>();
markedIds.reserve(items.size()); markedIds.reserve(items.size());
for (const auto &item : items) { for (const auto &item : items) {
if (!item->markContentsRead() || !item->isRegular() || true) { if (!item->markContentsRead(true) || !item->isRegular() || true) {
AssertIsDebug(); AssertIsDebug();
continue; continue;
} }
@ -1320,7 +1320,7 @@ void ApiWrap::markContentsRead(
} }
void ApiWrap::markContentsRead(not_null<HistoryItem*> item) { void ApiWrap::markContentsRead(not_null<HistoryItem*> item) {
if (!item->markContentsRead() || !item->isRegular()) { if (!item->markContentsRead(true) || !item->isRegular()) {
return; return;
} }
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id)); const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));

View file

@ -1486,6 +1486,12 @@ void Session::requestAnimationPlayInline(not_null<HistoryItem*> item) {
} }
} }
void Session::requestUnreadReactionsAnimation(not_null<HistoryItem*> item) {
enumerateItemViews(item, [&](not_null<ViewElement*> view) {
view->animateUnreadReactions();
});
}
rpl::producer<not_null<HistoryItem*>> Session::animationPlayInlineRequest() const { rpl::producer<not_null<HistoryItem*>> Session::animationPlayInlineRequest() const {
return _animationPlayInlineRequest.events(); return _animationPlayInlineRequest.events();
} }

View file

@ -253,6 +253,7 @@ public:
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const; [[nodiscard]] rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const;
void requestItemTextRefresh(not_null<HistoryItem*> item); void requestItemTextRefresh(not_null<HistoryItem*> item);
void requestAnimationPlayInline(not_null<HistoryItem*> item); void requestAnimationPlayInline(not_null<HistoryItem*> item);
void requestUnreadReactionsAnimation(not_null<HistoryItem*> item);
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> animationPlayInlineRequest() const; [[nodiscard]] rpl::producer<not_null<HistoryItem*>> animationPlayInlineRequest() const;
void notifyHistoryUnloaded(not_null<const History*> history); void notifyHistoryUnloaded(not_null<const History*> history);
[[nodiscard]] rpl::producer<not_null<const History*>> historyUnloaded() const; [[nodiscard]] rpl::producer<not_null<const History*>> historyUnloaded() const;

View file

@ -386,7 +386,7 @@ HistoryInner::HistoryInner(
return; return;
} else if (const auto view = item->mainView()) { } else if (const auto view = item->mainView()) {
if (const auto top = itemTop(view); top >= 0) { if (const auto top = itemTop(view); top >= 0) {
view->animateSendReaction({ view->animateReaction({
.emoji = reaction.emoji, .emoji = reaction.emoji,
.flyIcon = reaction.icon, .flyIcon = reaction.icon,
.flyFrom = reaction.geometry.translated(0, -top), .flyFrom = reaction.geometry.translated(0, -top),

View file

@ -383,8 +383,11 @@ void HistoryItem::markReactionsRead() {
history()->unreadReactions().erase(id); history()->unreadReactions().erase(id);
} }
bool HistoryItem::markContentsRead() { bool HistoryItem::markContentsRead(bool fromThisClient) {
if (hasUnreadReaction()) { if (hasUnreadReaction()) {
if (fromThisClient) {
history()->owner().requestUnreadReactionsAnimation(this);
}
markReactionsRead(); markReactionsRead();
return true; return true;
} else if (isUnreadMention() || isIncomingUnreadMedia()) { } else if (isUnreadMention() || isIncomingUnreadMedia()) {

View file

@ -150,7 +150,7 @@ public:
[[nodiscard]] bool hasUnreadMediaFlag() const; [[nodiscard]] bool hasUnreadMediaFlag() const;
void markReactionsRead(); void markReactionsRead();
void markMediaAndMentionRead(); void markMediaAndMentionRead();
bool markContentsRead(); bool markContentsRead(bool fromThisClient = false);
void setIsPinned(bool isPinned); void setIsPinned(bool isPinned);
// For edit media in history_message. // For edit media in history_message.

View file

@ -512,10 +512,10 @@ void BottomInfo::setReactionCount(Reaction &reaction, int count) {
: 0; : 0;
} }
void BottomInfo::animateReactionSend( void BottomInfo::animateReaction(
SendReactionAnimationArgs &&args, ReactionAnimationArgs &&args,
Fn<void()> repaint) { Fn<void()> repaint) {
_reactionAnimation = std::make_unique<Reactions::SendAnimation>( _reactionAnimation = std::make_unique<Reactions::Animation>(
_reactionsOwner, _reactionsOwner,
args.translated(QPoint(width(), height())), args.translated(QPoint(width(), height())),
std::move(repaint), std::move(repaint),
@ -523,12 +523,12 @@ void BottomInfo::animateReactionSend(
} }
auto BottomInfo::takeSendReactionAnimation() auto BottomInfo::takeSendReactionAnimation()
-> std::unique_ptr<Reactions::SendAnimation> { -> std::unique_ptr<Reactions::Animation> {
return std::move(_reactionAnimation); return std::move(_reactionAnimation);
} }
void BottomInfo::continueSendReactionAnimation( void BottomInfo::continueSendReactionAnimation(
std::unique_ptr<Reactions::SendAnimation> animation) { std::unique_ptr<Reactions::Animation> animation) {
_reactionAnimation = std::move(animation); _reactionAnimation = std::move(animation);
} }

View file

@ -21,14 +21,14 @@ class Reactions;
namespace HistoryView { namespace HistoryView {
namespace Reactions { namespace Reactions {
class SendAnimation; class Animation;
} // namespace Reactions } // namespace Reactions
using PaintContext = Ui::ChatPaintContext; using PaintContext = Ui::ChatPaintContext;
class Message; class Message;
struct TextState; struct TextState;
struct SendReactionAnimationArgs; struct ReactionAnimationArgs;
class BottomInfo final : public Object { class BottomInfo final : public Object {
public: public:
@ -73,13 +73,13 @@ public:
bool inverted, bool inverted,
const PaintContext &context) const; const PaintContext &context) const;
void animateReactionSend( void animateReaction(
SendReactionAnimationArgs &&args, ReactionAnimationArgs &&args,
Fn<void()> repaint); Fn<void()> repaint);
[[nodiscard]] auto takeSendReactionAnimation() [[nodiscard]] auto takeSendReactionAnimation()
-> std::unique_ptr<Reactions::SendAnimation>; -> std::unique_ptr<Reactions::Animation>;
void continueSendReactionAnimation( void continueSendReactionAnimation(
std::unique_ptr<Reactions::SendAnimation> animation); std::unique_ptr<Reactions::Animation> animation);
private: private:
struct Reaction { struct Reaction {
@ -124,7 +124,7 @@ private:
Ui::Text::String _replies; Ui::Text::String _replies;
std::vector<Reaction> _reactions; std::vector<Reaction> _reactions;
mutable ClickHandlerPtr _revokeLink; mutable ClickHandlerPtr _revokeLink;
mutable std::unique_ptr<Reactions::SendAnimation> _reactionAnimation; mutable std::unique_ptr<Reactions::Animation> _reactionAnimation;
int _reactionsMaxWidth = 0; int _reactionsMaxWidth = 0;
int _dateWidth = 0; int _dateWidth = 0;
bool _authorElided = false; bool _authorElided = false;

View file

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_groups.h" #include "data/data_groups.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_sponsored_messages.h" #include "data/data_sponsored_messages.h"
#include "data/data_message_reactions.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
@ -342,7 +343,7 @@ void DateBadge::paint(
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide); ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
} }
SendReactionAnimationArgs SendReactionAnimationArgs::translated( ReactionAnimationArgs ReactionAnimationArgs::translated(
QPoint point) const { QPoint point) const {
return { return {
.emoji = emoji, .emoji = emoji,
@ -1059,11 +1060,20 @@ void Element::clickHandlerPressedChanged(
} }
} }
void Element::animateSendReaction(SendReactionAnimationArgs &&args) { void Element::animateReaction(ReactionAnimationArgs &&args) {
}
void Element::animateUnreadReactions() {
const auto &recent = data()->recentReactions();
for (const auto &[emoji, list] : recent) {
if (ranges::contains(list, true, &Data::RecentReaction::unread)) {
animateReaction({ .emoji = emoji });
}
}
} }
auto Element::takeSendReactionAnimation() auto Element::takeSendReactionAnimation()
-> std::unique_ptr<Reactions::SendAnimation> { -> std::unique_ptr<Reactions::Animation> {
return nullptr; return nullptr;
} }

View file

@ -49,7 +49,7 @@ using PaintContext = Ui::ChatPaintContext;
namespace Reactions { namespace Reactions {
struct ButtonParameters; struct ButtonParameters;
class SendAnimation; class Animation;
} // namespace Reactions } // namespace Reactions
enum class Context : char { enum class Context : char {
@ -229,12 +229,12 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
}; };
struct SendReactionAnimationArgs { struct ReactionAnimationArgs {
QString emoji; QString emoji;
std::shared_ptr<Lottie::Icon> flyIcon; std::shared_ptr<Lottie::Icon> flyIcon;
QRect flyFrom; QRect flyFrom;
[[nodiscard]] SendReactionAnimationArgs translated(QPoint point) const; [[nodiscard]] ReactionAnimationArgs translated(QPoint point) const;
}; };
class Element class Element
@ -421,9 +421,10 @@ public:
[[nodiscard]] bool markSponsoredViewed(int shownFromTop) const; [[nodiscard]] bool markSponsoredViewed(int shownFromTop) const;
virtual void animateSendReaction(SendReactionAnimationArgs &&args); virtual void animateReaction(ReactionAnimationArgs &&args);
void animateUnreadReactions();
[[nodiscard]] virtual auto takeSendReactionAnimation() [[nodiscard]] virtual auto takeSendReactionAnimation()
-> std::unique_ptr<Reactions::SendAnimation>; -> std::unique_ptr<Reactions::Animation>;
virtual ~Element(); virtual ~Element();
@ -438,7 +439,7 @@ public:
static void Moused(Element *view); static void Moused(Element *view);
[[nodiscard]] static Element *Moused(); [[nodiscard]] static Element *Moused();
static void ClearGlobal(); static void ClearGlobal();
protected: protected:
void repaint() const; void repaint() const;

View file

@ -353,7 +353,7 @@ ListWidget::ListWidget(
return; return;
} else if (const auto view = viewForItem(item)) { } else if (const auto view = viewForItem(item)) {
if (const auto top = itemTop(view); top >= 0) { if (const auto top = itemTop(view); top >= 0) {
view->animateSendReaction({ view->animateReaction({
.emoji = reaction.emoji, .emoji = reaction.emoji,
.flyIcon = reaction.icon, .flyIcon = reaction.icon,
.flyFrom = reaction.geometry.translated(0, -top), .flyFrom = reaction.geometry.translated(0, -top),

View file

@ -326,7 +326,7 @@ void Message::applyGroupAdminChanges(
} }
} }
void Message::animateSendReaction(SendReactionAnimationArgs &&args) { void Message::animateReaction(ReactionAnimationArgs &&args) {
const auto item = message(); const auto item = message();
const auto media = this->media(); const auto media = this->media();
@ -353,12 +353,12 @@ void Message::animateSendReaction(SendReactionAnimationArgs &&args) {
: 0; : 0;
g.setHeight(g.height() - reactionsHeight); g.setHeight(g.height() - reactionsHeight);
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip); const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
_reactions->animateSend(args.translated(-reactionsPosition), repainter); _reactions->animate(args.translated(-reactionsPosition), repainter);
return; return;
} }
const auto animateInBottomInfo = [&](QPoint bottomRight) { const auto animateInBottomInfo = [&](QPoint bottomRight) {
_bottomInfo.animateReactionSend(args.translated(-bottomRight), repainter); _bottomInfo.animateReaction(args.translated(-bottomRight), repainter);
}; };
if (bubble) { if (bubble) {
auto entry = logEntryOriginal(); auto entry = logEntryOriginal();
@ -381,7 +381,7 @@ void Message::animateSendReaction(SendReactionAnimationArgs &&args) {
if (reactionsInBubble) { if (reactionsInBubble) {
trect.setHeight(trect.height() - reactionsHeight); trect.setHeight(trect.height() - reactionsHeight);
const auto reactionsPosition = QPoint(trect.left(), trect.top() + trect.height() + reactionsTop); const auto reactionsPosition = QPoint(trect.left(), trect.top() + trect.height() + reactionsTop);
_reactions->animateSend(args.translated(-reactionsPosition), repainter); _reactions->animate(args.translated(-reactionsPosition), repainter);
return; return;
} }
if (_viewButton) { if (_viewButton) {
@ -425,7 +425,7 @@ void Message::animateSendReaction(SendReactionAnimationArgs &&args) {
} }
auto Message::takeSendReactionAnimation() auto Message::takeSendReactionAnimation()
-> std::unique_ptr<Reactions::SendAnimation> { -> std::unique_ptr<Reactions::Animation> {
return _reactions return _reactions
? _reactions->takeSendAnimation() ? _reactions->takeSendAnimation()
: _bottomInfo.takeSendReactionAnimation(); : _bottomInfo.takeSendReactionAnimation();
@ -2163,7 +2163,7 @@ void Message::refreshReactions() {
strong->data()->toggleReaction(emoji); strong->data()->toggleReaction(emoji);
if (const auto now = weak.get()) { if (const auto now = weak.get()) {
if (now->data()->chosenReaction() == emoji) { if (now->data()->chosenReaction() == emoji) {
now->animateSendReaction({ now->animateReaction({
.emoji = emoji, .emoji = emoji,
}); });
} }

View file

@ -135,9 +135,9 @@ public:
void applyGroupAdminChanges( void applyGroupAdminChanges(
const base::flat_set<UserId> &changes) override; const base::flat_set<UserId> &changes) override;
void animateSendReaction(SendReactionAnimationArgs &&args) override; void animateReaction(ReactionAnimationArgs &&args) override;
auto takeSendReactionAnimation() auto takeSendReactionAnimation()
-> std::unique_ptr<Reactions::SendAnimation> override; -> std::unique_ptr<Reactions::Animation> override;
protected: protected:
void refreshDataIdHook() override; void refreshDataIdHook() override;

View file

@ -21,9 +21,9 @@ constexpr auto kFlyDuration = crl::time(300);
} // namespace } // namespace
SendAnimation::SendAnimation( Animation::Animation(
not_null<::Data::Reactions*> owner, not_null<::Data::Reactions*> owner,
SendReactionAnimationArgs &&args, ReactionAnimationArgs &&args,
Fn<void()> repaint, Fn<void()> repaint,
int size) int size)
: _owner(owner) : _owner(owner)
@ -66,9 +66,9 @@ SendAnimation::SendAnimation(
_valid = true; _valid = true;
} }
SendAnimation::~SendAnimation() = default; Animation::~Animation() = default;
QRect SendAnimation::paintGetArea( QRect Animation::paintGetArea(
QPainter &p, QPainter &p,
QPoint origin, QPoint origin,
QRect target) const { QRect target) const {
@ -105,7 +105,7 @@ QRect SendAnimation::paintGetArea(
return wide; return wide;
} }
int SendAnimation::computeParabolicTop( int Animation::computeParabolicTop(
int from, int from,
int to, int to,
float64 progress) const { float64 progress) const {
@ -141,12 +141,12 @@ int SendAnimation::computeParabolicTop(
return int(base::SafeRound(_cachedA * t * t + _cachedB * t + from)); return int(base::SafeRound(_cachedA * t * t + _cachedB * t + from));
} }
void SendAnimation::startAnimations() { void Animation::startAnimations() {
_center->animate([=] { callback(); }, 0, _center->framesCount() - 1); _center->animate([=] { callback(); }, 0, _center->framesCount() - 1);
_effect->animate([=] { callback(); }, 0, _effect->framesCount() - 1); _effect->animate([=] { callback(); }, 0, _effect->framesCount() - 1);
} }
void SendAnimation::flyCallback() { void Animation::flyCallback() {
if (!_fly.animating()) { if (!_fly.animating()) {
_flyIcon = nullptr; _flyIcon = nullptr;
startAnimations(); startAnimations();
@ -154,25 +154,25 @@ void SendAnimation::flyCallback() {
callback(); callback();
} }
void SendAnimation::callback() { void Animation::callback() {
if (_repaint) { if (_repaint) {
_repaint(); _repaint();
} }
} }
void SendAnimation::setRepaintCallback(Fn<void()> repaint) { void Animation::setRepaintCallback(Fn<void()> repaint) {
_repaint = std::move(repaint); _repaint = std::move(repaint);
} }
bool SendAnimation::flying() const { bool Animation::flying() const {
return (_flyIcon != nullptr); return (_flyIcon != nullptr);
} }
float64 SendAnimation::flyingProgress() const { float64 Animation::flyingProgress() const {
return _fly.value(1.); return _fly.value(1.);
} }
QString SendAnimation::playingAroundEmoji() const { QString Animation::playingAroundEmoji() const {
const auto finished = !_valid const auto finished = !_valid
|| (!_flyIcon && !_center->animating() && !_effect->animating()); || (!_flyIcon && !_center->animating() && !_effect->animating());
return finished ? QString() : _emoji; return finished ? QString() : _emoji;

View file

@ -18,19 +18,19 @@ class Reactions;
} // namespace Data } // namespace Data
namespace HistoryView { namespace HistoryView {
struct SendReactionAnimationArgs; struct ReactionAnimationArgs;
} // namespace HistoryView } // namespace HistoryView
namespace HistoryView::Reactions { namespace HistoryView::Reactions {
class SendAnimation final { class Animation final {
public: public:
SendAnimation( Animation(
not_null<::Data::Reactions*> owner, not_null<::Data::Reactions*> owner,
SendReactionAnimationArgs &&args, ReactionAnimationArgs &&args,
Fn<void()> repaint, Fn<void()> repaint,
int size); int size);
~SendAnimation(); ~Animation();
void setRepaintCallback(Fn<void()> repaint); void setRepaintCallback(Fn<void()> repaint);
QRect paintGetArea(QPainter &p, QPoint origin, QRect target) const; QRect paintGetArea(QPainter &p, QPoint origin, QRect target) const;

View file

@ -408,10 +408,10 @@ bool InlineList::getState(
return false; return false;
} }
void InlineList::animateSend( void InlineList::animate(
SendReactionAnimationArgs &&args, ReactionAnimationArgs &&args,
Fn<void()> repaint) { Fn<void()> repaint) {
_animation = std::make_unique<Reactions::SendAnimation>( _animation = std::make_unique<Reactions::Animation>(
_owner, _owner,
std::move(args), std::move(args),
std::move(repaint), std::move(repaint),
@ -447,12 +447,12 @@ void InlineList::resolveUserpicsImage(const Button &button) const {
kMaxRecentUserpics); kMaxRecentUserpics);
} }
std::unique_ptr<SendAnimation> InlineList::takeSendAnimation() { std::unique_ptr<Animation> InlineList::takeSendAnimation() {
return std::move(_animation); return std::move(_animation);
} }
void InlineList::continueSendAnimation( void InlineList::continueSendAnimation(
std::unique_ptr<SendAnimation> animation) { std::unique_ptr<Animation> animation) {
_animation = std::move(animation); _animation = std::move(animation);
} }

View file

@ -22,13 +22,13 @@ namespace HistoryView {
using PaintContext = Ui::ChatPaintContext; using PaintContext = Ui::ChatPaintContext;
class Message; class Message;
struct TextState; struct TextState;
struct SendReactionAnimationArgs; struct ReactionAnimationArgs;
struct UserpicInRow; struct UserpicInRow;
} // namespace HistoryView } // namespace HistoryView
namespace HistoryView::Reactions { namespace HistoryView::Reactions {
class SendAnimation; class Animation;
struct InlineListData { struct InlineListData {
enum class Flag : uchar { enum class Flag : uchar {
@ -72,11 +72,11 @@ public:
QPoint point, QPoint point,
not_null<TextState*> outResult) const; not_null<TextState*> outResult) const;
void animateSend( void animate(
SendReactionAnimationArgs &&args, ReactionAnimationArgs &&args,
Fn<void()> repaint); Fn<void()> repaint);
[[nodiscard]] std::unique_ptr<SendAnimation> takeSendAnimation(); [[nodiscard]] std::unique_ptr<Animation> takeSendAnimation();
void continueSendAnimation(std::unique_ptr<SendAnimation> animation); void continueSendAnimation(std::unique_ptr<Animation> animation);
private: private:
struct Userpics { struct Userpics {
@ -113,7 +113,7 @@ private:
std::vector<Button> _buttons; std::vector<Button> _buttons;
QSize _skipBlock; QSize _skipBlock;
mutable std::unique_ptr<SendAnimation> _animation; mutable std::unique_ptr<Animation> _animation;
}; };