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>>();
markedIds.reserve(items.size());
for (const auto &item : items) {
if (!item->markContentsRead() || !item->isRegular() || true) {
if (!item->markContentsRead(true) || !item->isRegular() || true) {
AssertIsDebug();
continue;
}
@ -1320,7 +1320,7 @@ void ApiWrap::markContentsRead(
}
void ApiWrap::markContentsRead(not_null<HistoryItem*> item) {
if (!item->markContentsRead() || !item->isRegular()) {
if (!item->markContentsRead(true) || !item->isRegular()) {
return;
}
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 {
return _animationPlayInlineRequest.events();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,14 +21,14 @@ class Reactions;
namespace HistoryView {
namespace Reactions {
class SendAnimation;
class Animation;
} // namespace Reactions
using PaintContext = Ui::ChatPaintContext;
class Message;
struct TextState;
struct SendReactionAnimationArgs;
struct ReactionAnimationArgs;
class BottomInfo final : public Object {
public:
@ -73,13 +73,13 @@ public:
bool inverted,
const PaintContext &context) const;
void animateReactionSend(
SendReactionAnimationArgs &&args,
void animateReaction(
ReactionAnimationArgs &&args,
Fn<void()> repaint);
[[nodiscard]] auto takeSendReactionAnimation()
-> std::unique_ptr<Reactions::SendAnimation>;
-> std::unique_ptr<Reactions::Animation>;
void continueSendReactionAnimation(
std::unique_ptr<Reactions::SendAnimation> animation);
std::unique_ptr<Reactions::Animation> animation);
private:
struct Reaction {
@ -124,7 +124,7 @@ private:
Ui::Text::String _replies;
std::vector<Reaction> _reactions;
mutable ClickHandlerPtr _revokeLink;
mutable std::unique_ptr<Reactions::SendAnimation> _reactionAnimation;
mutable std::unique_ptr<Reactions::Animation> _reactionAnimation;
int _reactionsMaxWidth = 0;
int _dateWidth = 0;
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_media_types.h"
#include "data/data_sponsored_messages.h"
#include "data/data_message_reactions.h"
#include "lang/lang_keys.h"
#include "styles/style_chat.h"
@ -342,7 +343,7 @@ void DateBadge::paint(
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
}
SendReactionAnimationArgs SendReactionAnimationArgs::translated(
ReactionAnimationArgs ReactionAnimationArgs::translated(
QPoint point) const {
return {
.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()
-> std::unique_ptr<Reactions::SendAnimation> {
-> std::unique_ptr<Reactions::Animation> {
return nullptr;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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