Show animation on story reaction sending.

This commit is contained in:
John Preston 2023-06-15 16:26:49 +04:00
parent 41eac3692c
commit ff835ec76c
8 changed files with 100 additions and 8 deletions

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/painter.h" #include "ui/painter.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_custom_emoji.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -667,12 +668,30 @@ ChosenReaction Selector::lookupChosen(const Data::ReactionId &id) const {
return result; return result;
} }
void Selector::preloadAllRecentsAnimations() {
const auto preload = [&](DocumentData *document) {
const auto view = document
? document->activeMediaView()
: nullptr;
if (view) {
view->checkStickerLarge();
}
};
for (const auto &reaction : _reactions.recent) {
if (!reaction.id.custom()) {
preload(reaction.centerIcon);
}
preload(reaction.aroundAnimation);
}
}
void Selector::expand() { void Selector::expand() {
if (_expandScheduled) { if (_expandScheduled) {
return; return;
} }
_expandScheduled = true; _expandScheduled = true;
_willExpand.fire({}); _willExpand.fire({});
preloadAllRecentsAnimations();
const auto parent = parentWidget()->geometry(); const auto parent = parentWidget()->geometry();
const auto extents = extentsForShadow(); const auto extents = extentsForShadow();
const auto heightLimit = _reactions.customAllowed const auto heightLimit = _reactions.customAllowed

View File

@ -131,6 +131,7 @@ private:
void createList(); void createList();
void finishExpand(); void finishExpand();
ChosenReaction lookupChosen(const Data::ReactionId &id) const; ChosenReaction lookupChosen(const Data::ReactionId &id) const;
void preloadAllRecentsAnimations();
const style::EmojiPan &_st; const style::EmojiPan &_st;
const std::shared_ptr<ChatHelpers::Show> _show; const std::shared_ptr<ChatHelpers::Show> _show;

View File

@ -11,11 +11,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/power_save_blocker.h" #include "base/power_save_blocker.h"
#include "base/qt_signal_producer.h" #include "base/qt_signal_producer.h"
#include "chat_helpers/compose/compose_show.h" #include "chat_helpers/compose/compose_show.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_message_reactions.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "history/view/reactions/history_view_reactions_strip.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "media/stories/media_stories_caption_full_view.h" #include "media/stories/media_stories_caption_full_view.h"
#include "media/stories/media_stories_delegate.h" #include "media/stories/media_stories_delegate.h"
@ -28,8 +31,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/stories/media_stories_share.h" #include "media/stories/media_stories_share.h"
#include "media/stories/media_stories_view.h" #include "media/stories/media_stories_view.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "ui/rp_widget.h" #include "ui/effects/emoji_fly_animation.h"
#include "ui/effects/message_sending_animation_common.h"
#include "ui/effects/reaction_fly_animation.h"
#include "ui/layers/box_content.h" #include "ui/layers/box_content.h"
#include "ui/rp_widget.h"
#include "styles/style_media_view.h" #include "styles/style_media_view.h"
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
#include "styles/style_boxes.h" // UserpicButton #include "styles/style_boxes.h" // UserpicButton
@ -171,8 +177,14 @@ Controller::Controller(not_null<Delegate*> delegate)
}, _lifetime); }, _lifetime);
_reactions->chosen( _reactions->chosen(
) | rpl::start_with_next([=](const Data::ReactionId &id) { ) | rpl::start_with_next([=](HistoryView::Reactions::ChosenReaction id) {
_replyArea->sendReaction(id); startReactionAnimation(id.id, {
.type = Ui::MessageSendingAnimationFrom::Type::Emoji,
.globalStartGeometry = id.globalGeometry,
.frame = id.icon,
});
_replyArea->sendReaction(id.id);
unfocusReply();
}, _lifetime); }, _lifetime);
_delegate->storiesLayerShown( _delegate->storiesLayerShown(
@ -1062,4 +1074,32 @@ void Controller::updatePowerSaveBlocker(const Player::TrackState &state) {
[=] { return _wrap->window()->windowHandle(); }); [=] { return _wrap->window()->windowHandle(); });
} }
void Controller::startReactionAnimation(
Data::ReactionId id,
Ui::MessageSendingAnimationFrom from) {
Expects(shown());
auto args = Ui::ReactionFlyAnimationArgs{
.id = id,
.flyIcon = from.frame,
.flyFrom = _wrap->mapFromGlobal(from.globalStartGeometry),
.scaleOutDuration = st::fadeWrapDuration * 2,
};
_reactionAnimation = std::make_unique<Ui::EmojiFlyAnimation>(
_wrap,
&shownUser()->owner().reactions(),
std::move(args),
[=] { _reactionAnimation->repaint(); },
Data::CustomEmojiSizeTag::Isolated);
const auto layer = _reactionAnimation->layer();
_wrap->paintRequest() | rpl::start_with_next([=] {
if (!_reactionAnimation->paintBadgeFrame(_wrap.get())) {
InvokeQueued(layer, [=] {
_reactionAnimation = nullptr;
_wrap->update();
});
}
}, layer->lifetime());
}
} // namespace Media::Stories } // namespace Media::Stories

View File

@ -21,6 +21,7 @@ struct FileChosen;
namespace Data { namespace Data {
struct FileOrigin; struct FileOrigin;
struct ReactionId;
} // namespace Data } // namespace Data
namespace HistoryView::Reactions { namespace HistoryView::Reactions {
@ -29,6 +30,8 @@ class CachedIconFactory;
namespace Ui { namespace Ui {
class RpWidget; class RpWidget;
struct MessageSendingAnimationFrom;
class EmojiFlyAnimation;
} // namespace Ui } // namespace Ui
namespace Main { namespace Main {
@ -183,6 +186,10 @@ private:
void checkMoveByDelta(); void checkMoveByDelta();
void loadMoreToList(); void loadMoreToList();
void startReactionAnimation(
Data::ReactionId id,
Ui::MessageSendingAnimationFrom from);
const not_null<Delegate*> _delegate; const not_null<Delegate*> _delegate;
rpl::variable<std::optional<Layout>> _layout; rpl::variable<std::optional<Layout>> _layout;
@ -226,6 +233,7 @@ private:
std::unique_ptr<Sibling> _siblingRight; std::unique_ptr<Sibling> _siblingRight;
std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker; std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;
std::unique_ptr<Ui::EmojiFlyAnimation> _reactionAnimation;
Main::Session *_session = nullptr; Main::Session *_session = nullptr;
rpl::lifetime _sessionLifetime; rpl::lifetime _sessionLifetime;

View File

@ -143,7 +143,7 @@ void Reactions::create() {
_selector->chosen( _selector->chosen(
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
HistoryView::Reactions::ChosenReaction reaction) { HistoryView::Reactions::ChosenReaction reaction) {
_chosen.fire_copy(reaction.id); _chosen.fire_copy(reaction);
hide(); hide();
}, _selector->lifetime()); }, _selector->lifetime());

View File

@ -15,6 +15,7 @@ struct ReactionId;
namespace HistoryView::Reactions { namespace HistoryView::Reactions {
class Selector; class Selector;
struct ChosenReaction;
} // namespace HistoryView::Reactions } // namespace HistoryView::Reactions
namespace Ui { namespace Ui {
@ -30,10 +31,11 @@ public:
explicit Reactions(not_null<Controller*> controller); explicit Reactions(not_null<Controller*> controller);
~Reactions(); ~Reactions();
using Chosen = HistoryView::Reactions::ChosenReaction;
[[nodiscard]] rpl::producer<bool> expandedValue() const { [[nodiscard]] rpl::producer<bool> expandedValue() const {
return _expanded.value(); return _expanded.value();
} }
[[nodiscard]] rpl::producer<Data::ReactionId> chosen() const { [[nodiscard]] rpl::producer<Chosen> chosen() const {
return _chosen.events(); return _chosen.events();
} }
@ -54,7 +56,7 @@ private:
std::unique_ptr<Ui::RpWidget> _parent; std::unique_ptr<Ui::RpWidget> _parent;
std::unique_ptr<HistoryView::Reactions::Selector> _selector; std::unique_ptr<HistoryView::Reactions::Selector> _selector;
std::vector<std::unique_ptr<Hiding>> _hiding; std::vector<std::unique_ptr<Hiding>> _hiding;
rpl::event_stream<Data::ReactionId> _chosen; rpl::event_stream<Chosen> _chosen;
Ui::Animations::Simple _showing; Ui::Animations::Simple _showing;
rpl::variable<float64> _shownValue; rpl::variable<float64> _shownValue;
rpl::variable<bool> _expanded; rpl::variable<bool> _expanded;

View File

@ -67,7 +67,8 @@ ReactionFlyAnimation::ReactionFlyAnimation(
Data::CustomEmojiSizeTag customSizeTag) Data::CustomEmojiSizeTag customSizeTag)
: _owner(owner) : _owner(owner)
, _repaint(std::move(repaint)) , _repaint(std::move(repaint))
, _flyFrom(args.flyFrom) { , _flyFrom(args.flyFrom)
, _scaleOutDuration(args.scaleOutDuration) {
const auto &list = owner->list(::Data::Reactions::Type::All); const auto &list = owner->list(::Data::Reactions::Type::All);
auto centerIcon = (DocumentData*)nullptr; auto centerIcon = (DocumentData*)nullptr;
auto aroundAnimation = (DocumentData*)nullptr; auto aroundAnimation = (DocumentData*)nullptr;
@ -135,7 +136,26 @@ QRect ReactionFlyAnimation::paintGetArea(
const QColor &colored, const QColor &colored,
QRect clip, QRect clip,
crl::time now) const { crl::time now) const {
if (_flyIcon.isNull()) { const auto scale = [&] {
const auto rate = _effect ? _effect->frameRate() : 0.;
if (!_scaleOutDuration || !rate) {
return 1.;
}
const auto left = _effect->framesCount() - _effect->frameIndex();
const auto duration = left * 1000. / rate;
return (duration < _scaleOutDuration)
? (duration / double(_scaleOutDuration))
: 1.;
}();
if (scale < 1.) {
const auto delta = ((1. - scale) / 2.) * target.size();
target = QRect(
target.topLeft() + QPoint(delta.width(), delta.height()),
target.size() * scale);
}
if (!_valid) {
return QRect();
} else if (_flyIcon.isNull()) {
const auto wide = QRect( const auto wide = QRect(
target.topLeft() - QPoint(target.width(), target.height()) / 2, target.topLeft() - QPoint(target.width(), target.height()) / 2,
target.size() * 2); target.size() * 2);

View File

@ -27,6 +27,7 @@ struct ReactionFlyAnimationArgs {
::Data::ReactionId id; ::Data::ReactionId id;
QImage flyIcon; QImage flyIcon;
QRect flyFrom; QRect flyFrom;
crl::time scaleOutDuration = 0;
[[nodiscard]] ReactionFlyAnimationArgs translated(QPoint point) const; [[nodiscard]] ReactionFlyAnimationArgs translated(QPoint point) const;
}; };
@ -102,6 +103,7 @@ private:
QRect _flyFrom; QRect _flyFrom;
float64 _centerSizeMultiplier = 0.; float64 _centerSizeMultiplier = 0.;
int _customSize = 0; int _customSize = 0;
crl::time _scaleOutDuration = 0;
bool _valid = false; bool _valid = false;
mutable Parabolic _cached; mutable Parabolic _cached;