mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Play generic animations for custom reactions.
This commit is contained in:
parent
47709884dd
commit
7d77e8a203
6 changed files with 171 additions and 42 deletions
|
@ -276,6 +276,39 @@ void Reactions::setFavorite(const ReactionId &id) {
|
||||||
applyFavorite(id);
|
applyFavorite(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DocumentData *Reactions::chooseGenericAnimation(
|
||||||
|
not_null<DocumentData*> custom) const {
|
||||||
|
const auto sticker = custom->sticker();
|
||||||
|
const auto i = sticker
|
||||||
|
? ranges::find(
|
||||||
|
_available,
|
||||||
|
::Data::ReactionId{ { sticker->alt } },
|
||||||
|
&::Data::Reaction::id)
|
||||||
|
: end(_available);
|
||||||
|
if (i != end(_available) && i->aroundAnimation) {
|
||||||
|
const auto view = i->aroundAnimation->createMediaView();
|
||||||
|
view->checkStickerLarge();
|
||||||
|
if (view->loaded()) {
|
||||||
|
return i->aroundAnimation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_genericAnimations.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
auto copy = _genericAnimations;
|
||||||
|
ranges::shuffle(copy);
|
||||||
|
const auto first = copy.front();
|
||||||
|
const auto view = first->createMediaView();
|
||||||
|
view->checkStickerLarge();
|
||||||
|
if (view->loaded()) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
const auto k = ranges::find_if(copy, [&](not_null<DocumentData*> value) {
|
||||||
|
return value->createMediaView()->loaded();
|
||||||
|
});
|
||||||
|
return (k != end(copy)) ? (*k) : first;
|
||||||
|
}
|
||||||
|
|
||||||
void Reactions::applyFavorite(const ReactionId &id) {
|
void Reactions::applyFavorite(const ReactionId &id) {
|
||||||
if (_favoriteId != id) {
|
if (_favoriteId != id) {
|
||||||
_favoriteId = id;
|
_favoriteId = id;
|
||||||
|
@ -324,11 +357,16 @@ void Reactions::preloadImageFor(const ReactionId &id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Reactions::preloadAnimationsFor(const ReactionId &id) {
|
void Reactions::preloadAnimationsFor(const ReactionId &id) {
|
||||||
const auto i = ranges::find(_available, id, &Reaction::id);
|
const auto custom = id.custom();
|
||||||
|
const auto document = custom ? _owner->document(custom).get() : nullptr;
|
||||||
|
const auto customSticker = document ? document->sticker() : nullptr;
|
||||||
|
const auto findId = custom
|
||||||
|
? ReactionId{ { customSticker ? customSticker->alt : QString() } }
|
||||||
|
: id;
|
||||||
|
const auto i = ranges::find(_available, findId, &Reaction::id);
|
||||||
if (i == end(_available)) {
|
if (i == end(_available)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto preload = [&](DocumentData *document) {
|
const auto preload = [&](DocumentData *document) {
|
||||||
const auto view = document
|
const auto view = document
|
||||||
? document->activeMediaView()
|
? document->activeMediaView()
|
||||||
|
@ -337,7 +375,10 @@ void Reactions::preloadAnimationsFor(const ReactionId &id) {
|
||||||
view->checkStickerLarge();
|
view->checkStickerLarge();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
preload(i->centerIcon);
|
|
||||||
|
if (!custom) {
|
||||||
|
preload(i->centerIcon);
|
||||||
|
}
|
||||||
preload(i->aroundAnimation);
|
preload(i->aroundAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,6 +557,26 @@ void Reactions::requestDefault() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reactions::requestGeneric() {
|
||||||
|
if (_genericRequestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto &api = _owner->session().api();
|
||||||
|
_genericRequestId = api.request(MTPmessages_GetStickerSet(
|
||||||
|
MTP_inputStickerSetEmojiGenericAnimations(),
|
||||||
|
MTP_int(0) // hash
|
||||||
|
)).done([=](const MTPmessages_StickerSet &result) {
|
||||||
|
_genericRequestId = 0;
|
||||||
|
result.match([&](const MTPDmessages_stickerSet &data) {
|
||||||
|
updateGeneric(data);
|
||||||
|
}, [](const MTPDmessages_stickerSetNotModified &) {
|
||||||
|
LOG(("API Error: Unexpected messages.stickerSetNotModified."));
|
||||||
|
});
|
||||||
|
}).fail([=] {
|
||||||
|
_genericRequestId = 0;
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void Reactions::updateTop(const MTPDmessages_reactions &data) {
|
void Reactions::updateTop(const MTPDmessages_reactions &data) {
|
||||||
_topHash = data.vhash().v;
|
_topHash = data.vhash().v;
|
||||||
_topIds = ListFromMTP(data);
|
_topIds = ListFromMTP(data);
|
||||||
|
@ -564,6 +625,26 @@ void Reactions::updateDefault(const MTPDmessages_availableReactions &data) {
|
||||||
defaultUpdated();
|
defaultUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Reactions::updateGeneric(const MTPDmessages_stickerSet &data) {
|
||||||
|
const auto oldCache = base::take(_genericCache);
|
||||||
|
const auto toCache = [&](not_null<DocumentData*> document) {
|
||||||
|
if (document->sticker()) {
|
||||||
|
_genericAnimations.push_back(document);
|
||||||
|
_genericCache.emplace(document, document->createMediaView());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto &list = data.vdocuments().v;
|
||||||
|
_genericAnimations.clear();
|
||||||
|
_genericAnimations.reserve(list.size());
|
||||||
|
_genericCache.reserve(list.size());
|
||||||
|
for (const auto &sticker : data.vdocuments().v) {
|
||||||
|
toCache(_owner->processDocument(sticker));
|
||||||
|
}
|
||||||
|
if (!_genericCache.empty()) {
|
||||||
|
_genericCache.front().second->checkStickerLarge();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Reactions::recentUpdated() {
|
void Reactions::recentUpdated() {
|
||||||
_topRefreshTimer.callOnce(kTopRequestDelay);
|
_topRefreshTimer.callOnce(kTopRequestDelay);
|
||||||
_recentUpdated.fire({});
|
_recentUpdated.fire({});
|
||||||
|
@ -572,6 +653,9 @@ void Reactions::recentUpdated() {
|
||||||
void Reactions::defaultUpdated() {
|
void Reactions::defaultUpdated() {
|
||||||
refreshTop();
|
refreshTop();
|
||||||
refreshRecent();
|
refreshRecent();
|
||||||
|
if (_genericAnimations.empty()) {
|
||||||
|
requestGeneric();
|
||||||
|
}
|
||||||
_defaultUpdated.fire({});
|
_defaultUpdated.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,8 @@ public:
|
||||||
[[nodiscard]] ReactionId favoriteId() const;
|
[[nodiscard]] ReactionId favoriteId() const;
|
||||||
[[nodiscard]] const Reaction *favorite() const;
|
[[nodiscard]] const Reaction *favorite() const;
|
||||||
void setFavorite(const ReactionId &id);
|
void setFavorite(const ReactionId &id);
|
||||||
|
[[nodiscard]] DocumentData *chooseGenericAnimation(
|
||||||
|
not_null<DocumentData*> custom) const;
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<> topUpdates() const;
|
[[nodiscard]] rpl::producer<> topUpdates() const;
|
||||||
[[nodiscard]] rpl::producer<> recentUpdates() const;
|
[[nodiscard]] rpl::producer<> recentUpdates() const;
|
||||||
|
@ -127,10 +129,12 @@ private:
|
||||||
void requestTop();
|
void requestTop();
|
||||||
void requestRecent();
|
void requestRecent();
|
||||||
void requestDefault();
|
void requestDefault();
|
||||||
|
void requestGeneric();
|
||||||
|
|
||||||
void updateTop(const MTPDmessages_reactions &data);
|
void updateTop(const MTPDmessages_reactions &data);
|
||||||
void updateRecent(const MTPDmessages_reactions &data);
|
void updateRecent(const MTPDmessages_reactions &data);
|
||||||
void updateDefault(const MTPDmessages_availableReactions &data);
|
void updateDefault(const MTPDmessages_availableReactions &data);
|
||||||
|
void updateGeneric(const MTPDmessages_stickerSet &data);
|
||||||
|
|
||||||
void recentUpdated();
|
void recentUpdated();
|
||||||
void defaultUpdated();
|
void defaultUpdated();
|
||||||
|
@ -166,12 +170,16 @@ private:
|
||||||
std::vector<Reaction> _top;
|
std::vector<Reaction> _top;
|
||||||
std::vector<ReactionId> _topIds;
|
std::vector<ReactionId> _topIds;
|
||||||
base::flat_set<ReactionId> _unresolvedTop;
|
base::flat_set<ReactionId> _unresolvedTop;
|
||||||
|
std::vector<not_null<DocumentData*>> _genericAnimations;
|
||||||
ReactionId _favoriteId;
|
ReactionId _favoriteId;
|
||||||
ReactionId _unresolvedFavoriteId;
|
ReactionId _unresolvedFavoriteId;
|
||||||
std::optional<Reaction> _favorite;
|
std::optional<Reaction> _favorite;
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
not_null<DocumentData*>,
|
not_null<DocumentData*>,
|
||||||
std::shared_ptr<DocumentMedia>> _iconsCache;
|
std::shared_ptr<DocumentMedia>> _iconsCache;
|
||||||
|
base::flat_map<
|
||||||
|
not_null<DocumentData*>,
|
||||||
|
std::shared_ptr<DocumentMedia>> _genericCache;
|
||||||
rpl::event_stream<> _topUpdated;
|
rpl::event_stream<> _topUpdated;
|
||||||
rpl::event_stream<> _recentUpdated;
|
rpl::event_stream<> _recentUpdated;
|
||||||
rpl::event_stream<> _defaultUpdated;
|
rpl::event_stream<> _defaultUpdated;
|
||||||
|
@ -193,6 +201,8 @@ private:
|
||||||
mtpRequestId _defaultRequestId = 0;
|
mtpRequestId _defaultRequestId = 0;
|
||||||
int32 _defaultHash = 0;
|
int32 _defaultHash = 0;
|
||||||
|
|
||||||
|
mtpRequestId _genericRequestId = 0;
|
||||||
|
|
||||||
base::flat_map<ReactionId, ImageSet> _images;
|
base::flat_map<ReactionId, ImageSet> _images;
|
||||||
rpl::lifetime _imagesLoadLifetime;
|
rpl::lifetime _imagesLoadLifetime;
|
||||||
bool _waitingForList = false;
|
bool _waitingForList = false;
|
||||||
|
|
|
@ -562,11 +562,13 @@ auto CustomEmojiManager::createLoaderWithSetId(
|
||||||
SizeTag tag,
|
SizeTag tag,
|
||||||
int sizeOverride
|
int sizeOverride
|
||||||
) -> LoaderWithSetId {
|
) -> LoaderWithSetId {
|
||||||
const auto sticker = document->sticker();
|
if (const auto sticker = document->sticker()) {
|
||||||
return {
|
return {
|
||||||
std::make_unique<CustomEmojiLoader>(document, tag, sizeOverride),
|
std::make_unique<CustomEmojiLoader>(document, tag, sizeOverride),
|
||||||
sticker ? sticker->set.id : uint64()
|
sticker->set.id,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
return createLoaderWithSetId(document->id, tag, sizeOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CustomEmojiManager::createLoaderWithSetId(
|
auto CustomEmojiManager::createLoaderWithSetId(
|
||||||
|
|
|
@ -326,6 +326,7 @@ void InlineList::paint(
|
||||||
};
|
};
|
||||||
std::vector<SingleAnimation> animations;
|
std::vector<SingleAnimation> animations;
|
||||||
|
|
||||||
|
auto finished = std::vector<std::unique_ptr<Animation>>();
|
||||||
const auto st = context.st;
|
const auto st = context.st;
|
||||||
const auto stm = context.messageStyle();
|
const auto stm = context.messageStyle();
|
||||||
const auto padding = st::reactionInlinePadding;
|
const auto padding = st::reactionInlinePadding;
|
||||||
|
@ -338,7 +339,8 @@ void InlineList::paint(
|
||||||
if (context.reactionInfo
|
if (context.reactionInfo
|
||||||
&& button.animation
|
&& button.animation
|
||||||
&& button.animation->finished()) {
|
&& button.animation->finished()) {
|
||||||
button.animation = nullptr;
|
// Let the animation (and its custom emoji) live while painting.
|
||||||
|
finished.push_back(std::move(button.animation));
|
||||||
}
|
}
|
||||||
const auto animating = (button.animation != nullptr);
|
const auto animating = (button.animation != nullptr);
|
||||||
const auto &geometry = button.geometry;
|
const auto &geometry = button.geometry;
|
||||||
|
|
|
@ -24,6 +24,26 @@ constexpr auto kFlyDuration = crl::time(300);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
auto Animation::flyCallback() {
|
||||||
|
return [=] {
|
||||||
|
if (!_fly.animating()) {
|
||||||
|
_flyIcon = QImage();
|
||||||
|
startAnimations();
|
||||||
|
}
|
||||||
|
if (_repaint) {
|
||||||
|
_repaint();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Animation::callback() {
|
||||||
|
return [=] {
|
||||||
|
if (_repaint) {
|
||||||
|
_repaint();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Animation::Animation(
|
Animation::Animation(
|
||||||
not_null<::Data::Reactions*> owner,
|
not_null<::Data::Reactions*> owner,
|
||||||
ReactionAnimationArgs &&args,
|
ReactionAnimationArgs &&args,
|
||||||
|
@ -37,11 +57,11 @@ Animation::Animation(
|
||||||
auto centerIconSize = size;
|
auto centerIconSize = size;
|
||||||
auto aroundAnimation = (DocumentData*)nullptr;
|
auto aroundAnimation = (DocumentData*)nullptr;
|
||||||
if (const auto customId = args.id.custom()) {
|
if (const auto customId = args.id.custom()) {
|
||||||
const auto document = owner->owner().document(customId);
|
centerIconSize = Ui::Text::AdjustCustomEmojiSize(st::emojiSize);
|
||||||
if (document->sticker()) {
|
const auto data = &owner->owner();
|
||||||
centerIcon = document;
|
const auto document = data->document(customId);
|
||||||
centerIconSize = Ui::Text::AdjustCustomEmojiSize(st::emojiSize);
|
_custom = data->customEmojiManager().create(document, callback());
|
||||||
}
|
aroundAnimation = owner->chooseGenericAnimation(document);
|
||||||
} else {
|
} else {
|
||||||
const auto i = ranges::find(list, args.id, &::Data::Reaction::id);
|
const auto i = ranges::find(list, args.id, &::Data::Reaction::id);
|
||||||
if (i == end(list) || !i->centerIcon) {
|
if (i == end(list) || !i->centerIcon) {
|
||||||
|
@ -67,17 +87,19 @@ Animation::Animation(
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
_flyIcon = std::move(args.flyIcon);
|
if (!_custom && !resolve(_center, centerIcon, centerIconSize)) {
|
||||||
_centerSizeMultiplier = centerIconSize / float64(size);
|
|
||||||
if (!resolve(_center, centerIcon, centerIconSize)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(_effect, aroundAnimation, size * 2);
|
resolve(_effect, aroundAnimation, size * 2);
|
||||||
if (!_flyIcon.isNull()) {
|
if (!args.flyIcon.isNull()) {
|
||||||
_fly.start([=] { flyCallback(); }, 0., 1., kFlyDuration);
|
_flyIcon = std::move(args.flyIcon);
|
||||||
|
_fly.start(flyCallback(), 0., 1., kFlyDuration);
|
||||||
|
} else if (!_center && !_effect) {
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
startAnimations();
|
startAnimations();
|
||||||
}
|
}
|
||||||
|
_centerSizeMultiplier = centerIconSize / float64(size);
|
||||||
_valid = true;
|
_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,16 +144,32 @@ QRect Animation::paintGetArea(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::paintCenterFrame(QPainter &p, QRect target) const {
|
void Animation::paintCenterFrame(QPainter &p, QRect target) const {
|
||||||
|
Expects(_center || _custom);
|
||||||
|
|
||||||
const auto size = QSize(
|
const auto size = QSize(
|
||||||
int(base::SafeRound(target.width() * _centerSizeMultiplier)),
|
int(base::SafeRound(target.width() * _centerSizeMultiplier)),
|
||||||
int(base::SafeRound(target.height() * _centerSizeMultiplier)));
|
int(base::SafeRound(target.height() * _centerSizeMultiplier)));
|
||||||
p.drawImage(
|
if (_center) {
|
||||||
QRect(
|
const auto rect = QRect(
|
||||||
target.x() + (target.width() - size.width()) / 2,
|
target.x() + (target.width() - size.width()) / 2,
|
||||||
target.y() + (target.height() - size.height()) / 2,
|
target.y() + (target.height() - size.height()) / 2,
|
||||||
size.width(),
|
size.width(),
|
||||||
size.height()),
|
size.height());
|
||||||
_center->frame());
|
p.drawImage(rect, _center->frame());
|
||||||
|
} else {
|
||||||
|
const auto side = Ui::Text::AdjustCustomEmojiSize(st::emojiSize);
|
||||||
|
const auto scaled = (size.width() != side);
|
||||||
|
_custom->paint(p, {
|
||||||
|
.preview = Qt::transparent,
|
||||||
|
.size = { side, side },
|
||||||
|
.now = crl::now(),
|
||||||
|
.scale = (scaled ? (size.width() / float64(side)) : 1.),
|
||||||
|
.position = QPoint(
|
||||||
|
target.x() + (target.width() - side) / 2,
|
||||||
|
target.y() + (target.height() - side) / 2),
|
||||||
|
.scaled = scaled,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Animation::computeParabolicTop(
|
int Animation::computeParabolicTop(
|
||||||
|
@ -171,23 +209,11 @@ int Animation::computeParabolicTop(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::startAnimations() {
|
void Animation::startAnimations() {
|
||||||
_center->animate([=] { callback(); });
|
if (const auto center = _center.get()) {
|
||||||
|
_center->animate(callback());
|
||||||
|
}
|
||||||
if (const auto effect = _effect.get()) {
|
if (const auto effect = _effect.get()) {
|
||||||
_effect->animate([=] { callback(); });
|
_effect->animate(callback());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Animation::flyCallback() {
|
|
||||||
if (!_fly.animating()) {
|
|
||||||
_flyIcon = QImage();
|
|
||||||
startAnimations();
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Animation::callback() {
|
|
||||||
if (_repaint) {
|
|
||||||
_repaint();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +232,7 @@ float64 Animation::flyingProgress() const {
|
||||||
bool Animation::finished() const {
|
bool Animation::finished() const {
|
||||||
return !_valid
|
return !_valid
|
||||||
|| (_flyIcon.isNull()
|
|| (_flyIcon.isNull()
|
||||||
&& !_center->animating()
|
&& (!_center || !_center->animating())
|
||||||
&& (!_effect || !_effect->animating()));
|
&& (!_effect || !_effect->animating()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,10 @@ namespace Ui {
|
||||||
class AnimatedIcon;
|
class AnimatedIcon;
|
||||||
} // namespace Lottie
|
} // namespace Lottie
|
||||||
|
|
||||||
|
namespace Ui::Text {
|
||||||
|
class CustomEmoji;
|
||||||
|
} // namespace Ui::Text
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Reactions;
|
class Reactions;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
@ -40,15 +44,16 @@ public:
|
||||||
[[nodiscard]] bool finished() const;
|
[[nodiscard]] bool finished() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void flyCallback();
|
[[nodiscard]] auto flyCallback();
|
||||||
|
[[nodiscard]] auto callback();
|
||||||
void startAnimations();
|
void startAnimations();
|
||||||
void callback();
|
|
||||||
int computeParabolicTop(int from, int to, float64 progress) const;
|
int computeParabolicTop(int from, int to, float64 progress) const;
|
||||||
void paintCenterFrame(QPainter &p, QRect target) const;
|
void paintCenterFrame(QPainter &p, QRect target) const;
|
||||||
|
|
||||||
const not_null<::Data::Reactions*> _owner;
|
const not_null<::Data::Reactions*> _owner;
|
||||||
Fn<void()> _repaint;
|
Fn<void()> _repaint;
|
||||||
QImage _flyIcon;
|
QImage _flyIcon;
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji> _custom;
|
||||||
std::unique_ptr<Ui::AnimatedIcon> _center;
|
std::unique_ptr<Ui::AnimatedIcon> _center;
|
||||||
std::unique_ptr<Ui::AnimatedIcon> _effect;
|
std::unique_ptr<Ui::AnimatedIcon> _effect;
|
||||||
Ui::Animations::Simple _fly;
|
Ui::Animations::Simple _fly;
|
||||||
|
|
Loading…
Add table
Reference in a new issue