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 "history/history_item.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_session.h"
#include "data/stickers/data_custom_emoji.h"
#include "main/main_session.h"
@ -667,12 +668,30 @@ ChosenReaction Selector::lookupChosen(const Data::ReactionId &id) const {
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() {
if (_expandScheduled) {
return;
}
_expandScheduled = true;
_willExpand.fire({});
preloadAllRecentsAnimations();
const auto parent = parentWidget()->geometry();
const auto extents = extentsForShadow();
const auto heightLimit = _reactions.customAllowed

View File

@ -131,6 +131,7 @@ private:
void createList();
void finishExpand();
ChosenReaction lookupChosen(const Data::ReactionId &id) const;
void preloadAllRecentsAnimations();
const style::EmojiPan &_st;
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/qt_signal_producer.h"
#include "chat_helpers/compose/compose_show.h"
#include "data/stickers/data_custom_emoji.h"
#include "data/data_changes.h"
#include "data/data_file_origin.h"
#include "data/data_message_reactions.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_user.h"
#include "history/view/reactions/history_view_reactions_strip.h"
#include "main/main_session.h"
#include "media/stories/media_stories_caption_full_view.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_view.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/rp_widget.h"
#include "styles/style_media_view.h"
#include "styles/style_widgets.h"
#include "styles/style_boxes.h" // UserpicButton
@ -171,8 +177,14 @@ Controller::Controller(not_null<Delegate*> delegate)
}, _lifetime);
_reactions->chosen(
) | rpl::start_with_next([=](const Data::ReactionId &id) {
_replyArea->sendReaction(id);
) | rpl::start_with_next([=](HistoryView::Reactions::ChosenReaction id) {
startReactionAnimation(id.id, {
.type = Ui::MessageSendingAnimationFrom::Type::Emoji,
.globalStartGeometry = id.globalGeometry,
.frame = id.icon,
});
_replyArea->sendReaction(id.id);
unfocusReply();
}, _lifetime);
_delegate->storiesLayerShown(
@ -1062,4 +1074,32 @@ void Controller::updatePowerSaveBlocker(const Player::TrackState &state) {
[=] { 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

View File

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

View File

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

View File

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

View File

@ -67,7 +67,8 @@ ReactionFlyAnimation::ReactionFlyAnimation(
Data::CustomEmojiSizeTag customSizeTag)
: _owner(owner)
, _repaint(std::move(repaint))
, _flyFrom(args.flyFrom) {
, _flyFrom(args.flyFrom)
, _scaleOutDuration(args.scaleOutDuration) {
const auto &list = owner->list(::Data::Reactions::Type::All);
auto centerIcon = (DocumentData*)nullptr;
auto aroundAnimation = (DocumentData*)nullptr;
@ -135,7 +136,26 @@ QRect ReactionFlyAnimation::paintGetArea(
const QColor &colored,
QRect clip,
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(
target.topLeft() - QPoint(target.width(), target.height()) / 2,
target.size() * 2);

View File

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