mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 21:27:07 +02:00
Implement basic effect animation.
This commit is contained in:
parent
f762634036
commit
a19e71324b
19 changed files with 258 additions and 19 deletions
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_changes.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
|
@ -594,6 +595,9 @@ void Reactions::preloadImageFor(const ReactionId &id) {
|
|||
} else {
|
||||
generateImage(set, i->title);
|
||||
}
|
||||
if (set.effect) {
|
||||
preloadEffect(*i);
|
||||
}
|
||||
} else if (set.effect && !_waitingForEffects) {
|
||||
_waitingForEffects = true;
|
||||
refreshEffects();
|
||||
|
@ -603,6 +607,15 @@ void Reactions::preloadImageFor(const ReactionId &id) {
|
|||
}
|
||||
}
|
||||
|
||||
void Reactions::preloadEffect(const Reaction &effect) {
|
||||
if (effect.aroundAnimation) {
|
||||
effect.aroundAnimation->createMediaView()->checkStickerLarge();
|
||||
} else {
|
||||
const auto premium = effect.selectAnimation;
|
||||
premium->loadVideoThumbnail(premium->stickerSetOrigin());
|
||||
}
|
||||
}
|
||||
|
||||
void Reactions::preloadAnimationsFor(const ReactionId &id) {
|
||||
const auto custom = id.custom();
|
||||
const auto document = custom ? _owner->document(custom).get() : nullptr;
|
||||
|
|
|
@ -212,6 +212,7 @@ private:
|
|||
[[nodiscard]] std::optional<Reaction> parse(
|
||||
const MTPAvailableEffect &entry);
|
||||
|
||||
void preloadEffect(const Reaction &effect);
|
||||
void preloadImageFor(const ReactionId &id);
|
||||
[[nodiscard]] QImage resolveImageFor(const ReactionId &id);
|
||||
void loadImage(
|
||||
|
|
|
@ -321,6 +321,8 @@ enum class MessageFlag : uint64 {
|
|||
ReactionsAreTags = (1ULL << 43),
|
||||
|
||||
ShortcutMessage = (1ULL << 44),
|
||||
|
||||
EffectWatchedLocal = (1ULL << 45),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
|
|
@ -674,6 +674,11 @@ void InnerWidget::elementStartPremium(
|
|||
void InnerWidget::elementCancelPremium(not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void InnerWidget::elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) {
|
||||
}
|
||||
|
||||
QString InnerWidget::elementAuthorRank(not_null<const Element*> view) {
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -136,6 +136,9 @@ public:
|
|||
HistoryView::Element *replacing) override;
|
||||
void elementCancelPremium(
|
||||
not_null<const HistoryView::Element*> view) override;
|
||||
void elementStartEffect(
|
||||
not_null<const HistoryView::Element*> view,
|
||||
HistoryView::Element *replacing) override;
|
||||
QString elementAuthorRank(
|
||||
not_null<const HistoryView::Element*> view) override;
|
||||
|
||||
|
|
|
@ -304,12 +304,18 @@ public:
|
|||
_widget->elementStartPremium(view, replacing);
|
||||
}
|
||||
}
|
||||
|
||||
void elementCancelPremium(not_null<const Element*> view) override {
|
||||
if (_widget) {
|
||||
_widget->elementCancelPremium(view);
|
||||
}
|
||||
}
|
||||
void elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) override {
|
||||
if (_widget) {
|
||||
_widget->elementStartEffect(view, replacing);
|
||||
}
|
||||
}
|
||||
|
||||
QString elementAuthorRank(not_null<const Element*> view) override {
|
||||
return {};
|
||||
|
@ -950,6 +956,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
_translateTracker->startBunch();
|
||||
auto readTill = (HistoryItem*)nullptr;
|
||||
auto readContents = base::flat_set<not_null<HistoryItem*>>();
|
||||
auto startEffects = base::flat_set<not_null<const Element*>>();
|
||||
const auto markingAsViewed = _widget->markingContentsRead();
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (_pinnedItem) {
|
||||
|
@ -958,6 +965,11 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
_translateTracker->finishBunch();
|
||||
if (readTill && _widget->markingMessagesRead()) {
|
||||
session().data().histories().readInboxTill(readTill);
|
||||
if (!startEffects.empty()) {
|
||||
for (const auto &view : startEffects) {
|
||||
_emojiInteractions->playEffectOnRead(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (markingAsViewed && !readContents.empty()) {
|
||||
session().api().markContentsRead(readContents);
|
||||
|
@ -991,6 +1003,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
session().sponsoredMessages().view(item->fullId());
|
||||
} else if (isUnread) {
|
||||
readTill = item;
|
||||
if (item->hasUnwatchedEffect()) {
|
||||
startEffects.emplace(view);
|
||||
}
|
||||
}
|
||||
if (markingAsViewed && item->hasViews()) {
|
||||
session().api().views().scheduleIncrement(item);
|
||||
|
@ -3568,6 +3583,12 @@ void HistoryInner::elementCancelPremium(not_null<const Element*> view) {
|
|||
_emojiInteractions->cancelPremiumEffect(view);
|
||||
}
|
||||
|
||||
void HistoryInner::elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) {
|
||||
_emojiInteractions->playEffect(view);
|
||||
}
|
||||
|
||||
auto HistoryInner::getSelectionState() const
|
||||
-> HistoryView::TopBarWidget::SelectedState {
|
||||
auto result = HistoryView::TopBarWidget::SelectedState {};
|
||||
|
|
|
@ -170,6 +170,9 @@ public:
|
|||
not_null<const Element*> view,
|
||||
Element *replacing);
|
||||
void elementCancelPremium(not_null<const Element*> view);
|
||||
void elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing);
|
||||
|
||||
void updateBotInfo(bool recount = true);
|
||||
|
||||
|
|
|
@ -687,6 +687,9 @@ HistoryItem::HistoryItem(
|
|||
if (isHistoryEntry() && IsClientMsgId(id)) {
|
||||
_history->registerClientSideMessage(this);
|
||||
}
|
||||
if (_effectId) {
|
||||
_history->owner().reactions().preloadEffectImageFor(_effectId);
|
||||
}
|
||||
}
|
||||
|
||||
HistoryItem::HistoryItem(
|
||||
|
@ -1283,6 +1286,21 @@ bool HistoryItem::hasUnreadReaction() const {
|
|||
return (_flags & MessageFlag::HasUnreadReaction);
|
||||
}
|
||||
|
||||
bool HistoryItem::hasUnwatchedEffect() const {
|
||||
return !out()
|
||||
&& effectId()
|
||||
&& !(_flags & MessageFlag::EffectWatchedLocal)
|
||||
&& unread(history());
|
||||
}
|
||||
|
||||
bool HistoryItem::markEffectWatched() {
|
||||
if (!hasUnwatchedEffect()) {
|
||||
return false;
|
||||
}
|
||||
_flags |= MessageFlag::EffectWatchedLocal;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HistoryItem::mentionsMe() const {
|
||||
if (Has<HistoryServicePinned>()
|
||||
&& !Core::App().settings().notifyAboutPinned()) {
|
||||
|
|
|
@ -241,6 +241,8 @@ public:
|
|||
[[nodiscard]] bool mentionsMe() const;
|
||||
[[nodiscard]] bool isUnreadMention() const;
|
||||
[[nodiscard]] bool hasUnreadReaction() const;
|
||||
[[nodiscard]] bool hasUnwatchedEffect() const;
|
||||
bool markEffectWatched();
|
||||
[[nodiscard]] bool isUnreadMedia() const;
|
||||
[[nodiscard]] bool isIncomingUnreadMedia() const;
|
||||
[[nodiscard]] bool hasUnreadMediaFlag() const;
|
||||
|
|
|
@ -1389,7 +1389,10 @@ int HistoryWidget::itemTopForHighlight(
|
|||
const auto itemTop = _list->itemTop(view);
|
||||
Assert(itemTop >= 0);
|
||||
|
||||
const auto reactionCenter = view->data()->hasUnreadReaction()
|
||||
const auto item = view->data();
|
||||
const auto unwatchedEffect = item->hasUnwatchedEffect();
|
||||
const auto showReactions = item->hasUnreadReaction() || unwatchedEffect;
|
||||
const auto reactionCenter = showReactions
|
||||
? view->reactionButtonParameters({}, {}).center.y()
|
||||
: -1;
|
||||
|
||||
|
@ -2376,8 +2379,6 @@ void HistoryWidget::showHistory(
|
|||
}
|
||||
}
|
||||
|
||||
session().data().reactions().refreshEffects();
|
||||
|
||||
_scroll->hide();
|
||||
_list = _scroll->setOwnedWidget(
|
||||
object_ptr<HistoryInner>(this, _scroll, controller(), _history));
|
||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "chat_helpers/emoji_interactions.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "main/main_session.h"
|
||||
#include "lottie/lottie_icon.h"
|
||||
|
@ -104,10 +105,11 @@ bool BottomInfo::isWide() const {
|
|||
}
|
||||
|
||||
TextState BottomInfo::textState(
|
||||
not_null<const HistoryItem*> item,
|
||||
not_null<const Message*> view,
|
||||
QPoint position) const {
|
||||
const auto item = view->data();
|
||||
auto result = TextState(item);
|
||||
if (const auto link = replayEffectLink(item, position)) {
|
||||
if (const auto link = replayEffectLink(view, position)) {
|
||||
result.link = link;
|
||||
return result;
|
||||
}
|
||||
|
@ -158,7 +160,7 @@ TextState BottomInfo::textState(
|
|||
}
|
||||
|
||||
ClickHandlerPtr BottomInfo::replayEffectLink(
|
||||
not_null<const HistoryItem*> item,
|
||||
not_null<const Message*> view,
|
||||
QPoint position) const {
|
||||
if (!_effect) {
|
||||
return nullptr;
|
||||
|
@ -189,7 +191,7 @@ ClickHandlerPtr BottomInfo::replayEffectLink(
|
|||
st::msgDateFont->height);
|
||||
if (image.contains(position)) {
|
||||
if (!_replayLink) {
|
||||
_replayLink = replayEffectLink(item);
|
||||
_replayLink = replayEffectLink(view);
|
||||
}
|
||||
return _replayLink;
|
||||
}
|
||||
|
@ -200,15 +202,17 @@ ClickHandlerPtr BottomInfo::replayEffectLink(
|
|||
}
|
||||
|
||||
ClickHandlerPtr BottomInfo::replayEffectLink(
|
||||
not_null<const HistoryItem*> item) const {
|
||||
not_null<const Message*> view) const {
|
||||
const auto item = view->data();
|
||||
const auto itemId = item->fullId();
|
||||
const auto sessionId = item->history()->session().uniqueId();
|
||||
return std::make_shared<LambdaClickHandler>([=](
|
||||
ClickContext context) {
|
||||
const auto weak = base::make_weak(view);
|
||||
return std::make_shared<LambdaClickHandler>([=](ClickContext context) {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
controller->showToast("playing nice effect..");
|
||||
AssertIsDebug();
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->delegate()->elementStartEffect(strong, nullptr);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
[[nodiscard]] int firstLineWidth() const;
|
||||
[[nodiscard]] bool isWide() const;
|
||||
[[nodiscard]] TextState textState(
|
||||
not_null<const HistoryItem*> item,
|
||||
not_null<const Message*> view,
|
||||
QPoint position) const;
|
||||
[[nodiscard]] bool isSignedAuthorElided() const;
|
||||
|
||||
|
@ -106,10 +106,10 @@ private:
|
|||
|
||||
[[nodiscard]] Effect prepareEffectWithId(EffectId id);
|
||||
[[nodiscard]] ClickHandlerPtr replayEffectLink(
|
||||
not_null<const HistoryItem*> item,
|
||||
not_null<const Message*> view,
|
||||
QPoint position) const;
|
||||
[[nodiscard]] ClickHandlerPtr replayEffectLink(
|
||||
not_null<const HistoryItem*> item) const;
|
||||
not_null<const Message*> view) const;
|
||||
|
||||
const not_null<::Data::Reactions*> _reactionsOwner;
|
||||
Data _data;
|
||||
|
|
|
@ -192,6 +192,11 @@ void DefaultElementDelegate::elementCancelPremium(
|
|||
not_null<const Element*> view) {
|
||||
}
|
||||
|
||||
void DefaultElementDelegate::elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) {
|
||||
}
|
||||
|
||||
QString DefaultElementDelegate::elementAuthorRank(
|
||||
not_null<const Element*> view) {
|
||||
return {};
|
||||
|
|
|
@ -113,6 +113,9 @@ public:
|
|||
not_null<const Element*> view,
|
||||
Element *replacing) = 0;
|
||||
virtual void elementCancelPremium(not_null<const Element*> view) = 0;
|
||||
virtual void elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) = 0;
|
||||
virtual QString elementAuthorRank(not_null<const Element*> view) = 0;
|
||||
|
||||
virtual ~ElementDelegate() {
|
||||
|
@ -163,6 +166,9 @@ public:
|
|||
not_null<const Element*> view,
|
||||
Element *replacing) override;
|
||||
void elementCancelPremium(not_null<const Element*> view) override;
|
||||
void elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) override;
|
||||
QString elementAuthorRank(not_null<const Element*> view) override;
|
||||
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/media/history_view_sticker.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "chat_helpers/stickers_emoji_pack.h"
|
||||
#include "chat_helpers/emoji_interactions.h"
|
||||
#include "chat_helpers/stickers_lottie.h"
|
||||
|
@ -17,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_session.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_message_reactions.h"
|
||||
#include "lottie/lottie_common.h"
|
||||
#include "lottie/lottie_single_player.h"
|
||||
#include "base/random.h"
|
||||
|
@ -45,8 +47,8 @@ constexpr auto kDropDelayedAfterDelay = crl::time(2000);
|
|||
EmojiInteractions::EmojiInteractions(
|
||||
not_null<Main::Session*> session,
|
||||
Fn<int(not_null<const Element*>)> itemTop)
|
||||
: _session(session)
|
||||
, _itemTop(std::move(itemTop)) {
|
||||
: _session(session)
|
||||
, _itemTop(std::move(itemTop)) {
|
||||
_session->data().viewRemoved(
|
||||
) | rpl::filter([=] {
|
||||
return !_plays.empty() || !_delayed.empty();
|
||||
|
@ -56,6 +58,11 @@ EmojiInteractions::EmojiInteractions(
|
|||
ranges::remove(_delayed, view, &Delayed::view),
|
||||
end(_delayed));
|
||||
}, _lifetime);
|
||||
|
||||
_session->data().reactions().effectsUpdates(
|
||||
) | rpl::start_with_next([=] {
|
||||
checkPendingEffects();
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
EmojiInteractions::~EmojiInteractions() = default;
|
||||
|
@ -143,6 +150,121 @@ void EmojiInteractions::play(
|
|||
false);
|
||||
}
|
||||
|
||||
void EmojiInteractions::playEffectOnRead(not_null<const Element*> view) {
|
||||
if (view->data()->markEffectWatched()) {
|
||||
playEffect(view);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiInteractions::playEffect(not_null<const Element*> view) {
|
||||
if (const auto resolved = resolveEffect(view)) {
|
||||
playEffect(view, resolved);
|
||||
} else if (view->data()->effectId()) {
|
||||
if (resolved.document && !_downloadLifetime) {
|
||||
_downloadLifetime = _session->downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
checkPendingEffects();
|
||||
});
|
||||
}
|
||||
addPendingEffect(view);
|
||||
}
|
||||
}
|
||||
|
||||
EmojiInteractions::ResolvedEffect EmojiInteractions::resolveEffect(
|
||||
not_null<const Element*> view) {
|
||||
const auto item = view->data();
|
||||
const auto effectId = item->effectId();
|
||||
if (!effectId) {
|
||||
return {};
|
||||
}
|
||||
using Type = Data::Reactions::Type;
|
||||
const auto &effects = _session->data().reactions().list(Type::Effects);
|
||||
const auto i = ranges::find(
|
||||
effects,
|
||||
Data::ReactionId{ effectId },
|
||||
&Data::Reaction::id);
|
||||
if (i == end(effects)) {
|
||||
return {};
|
||||
}
|
||||
auto document = (DocumentData*)nullptr;
|
||||
auto content = QByteArray();
|
||||
auto filepath = QString();
|
||||
if ((document = i->aroundAnimation)) {
|
||||
content = document->createMediaView()->bytes();
|
||||
filepath = document->filepath();
|
||||
} else {
|
||||
document = i->selectAnimation;
|
||||
content = document->createMediaView()->videoThumbnailContent();
|
||||
}
|
||||
return {
|
||||
.emoticon = i->title,
|
||||
.document = document,
|
||||
.content = content,
|
||||
.filepath = filepath,
|
||||
};
|
||||
}
|
||||
|
||||
void EmojiInteractions::playEffect(
|
||||
not_null<const Element*> view,
|
||||
const ResolvedEffect &resolved) {
|
||||
play(
|
||||
resolved.emoticon,
|
||||
view,
|
||||
resolved.document,
|
||||
resolved.content,
|
||||
resolved.filepath,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
void EmojiInteractions::addPendingEffect(not_null<const Element*> view) {
|
||||
auto found = false;
|
||||
const auto predicate = [&](base::weak_ptr<const Element> weak) {
|
||||
const auto strong = weak.get();
|
||||
if (strong == view) {
|
||||
found = true;
|
||||
}
|
||||
return !strong;
|
||||
};
|
||||
_pendingEffects.erase(
|
||||
ranges::remove_if(_pendingEffects, predicate),
|
||||
end(_pendingEffects));
|
||||
if (!found) {
|
||||
_pendingEffects.push_back(view);
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiInteractions::checkPendingEffects() {
|
||||
auto waitingDownload = false;
|
||||
const auto predicate = [&](base::weak_ptr<const Element> weak) {
|
||||
const auto strong = weak.get();
|
||||
if (!strong) {
|
||||
return true;
|
||||
}
|
||||
const auto resolved = resolveEffect(strong);
|
||||
if (resolved) {
|
||||
playEffect(strong, resolved);
|
||||
return true;
|
||||
} else if (!strong->data()->effectId()) {
|
||||
return true;
|
||||
} else if (resolved.document) {
|
||||
waitingDownload = true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
_pendingEffects.erase(
|
||||
ranges::remove_if(_pendingEffects, predicate),
|
||||
end(_pendingEffects));
|
||||
if (!waitingDownload) {
|
||||
_downloadLifetime.destroy();
|
||||
} else if (!_downloadLifetime) {
|
||||
_downloadLifetime = _session->downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
checkPendingEffects();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EmojiInteractions::play(
|
||||
QString emoticon,
|
||||
not_null<const Element*> view,
|
||||
|
|
|
@ -43,6 +43,9 @@ public:
|
|||
void cancelPremiumEffect(not_null<const Element*> view);
|
||||
void visibleAreaUpdated(int visibleTop, int visibleBottom);
|
||||
|
||||
void playEffectOnRead(not_null<const Element*> view);
|
||||
void playEffect(not_null<const Element*> view);
|
||||
|
||||
void paint(QPainter &p);
|
||||
[[nodiscard]] rpl::producer<QRect> updateRequests() const;
|
||||
[[nodiscard]] rpl::producer<QString> playStarted() const;
|
||||
|
@ -68,6 +71,16 @@ private:
|
|||
crl::time shouldHaveStartedAt = 0;
|
||||
bool incoming = false;
|
||||
};
|
||||
struct ResolvedEffect {
|
||||
QString emoticon;
|
||||
DocumentData *document = nullptr;
|
||||
QByteArray content;
|
||||
QString filepath;
|
||||
|
||||
explicit operator bool() const {
|
||||
return document && (!content.isEmpty() || !filepath.isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] QRect computeRect(const Play &play) const;
|
||||
|
||||
|
@ -85,6 +98,14 @@ private:
|
|||
bool incoming,
|
||||
bool premium);
|
||||
void checkDelayed();
|
||||
void addPendingEffect(not_null<const Element*> view);
|
||||
|
||||
[[nodiscard]] ResolvedEffect resolveEffect(
|
||||
not_null<const Element*> view);
|
||||
void playEffect(
|
||||
not_null<const Element*> view,
|
||||
const ResolvedEffect &resolved);
|
||||
void checkPendingEffects();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
const Fn<int(not_null<const Element*>)> _itemTop;
|
||||
|
@ -97,6 +118,9 @@ private:
|
|||
rpl::event_stream<QRect> _updateRequests;
|
||||
rpl::event_stream<QString> _playStarted;
|
||||
|
||||
std::vector<base::weak_ptr<const Element>> _pendingEffects;
|
||||
rpl::lifetime _downloadLifetime;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -1857,6 +1857,12 @@ void ListWidget::elementCancelPremium(not_null<const Element*> view) {
|
|||
_emojiInteractions->cancelPremiumEffect(view);
|
||||
}
|
||||
|
||||
void ListWidget::elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) {
|
||||
_emojiInteractions->playEffect(view);
|
||||
}
|
||||
|
||||
QString ListWidget::elementAuthorRank(not_null<const Element*> view) {
|
||||
return _delegate->listElementAuthorRank(view);
|
||||
}
|
||||
|
|
|
@ -419,6 +419,9 @@ public:
|
|||
not_null<const Element*> view,
|
||||
Element *replacing) override;
|
||||
void elementCancelPremium(not_null<const Element*> view) override;
|
||||
void elementStartEffect(
|
||||
not_null<const Element*> view,
|
||||
Element *replacing) override;
|
||||
QString elementAuthorRank(not_null<const Element*> view) override;
|
||||
|
||||
void setEmptyInfoWidget(base::unique_qptr<Ui::RpWidget> &&w);
|
||||
|
|
|
@ -3038,7 +3038,7 @@ TextState Message::bottomInfoTextState(
|
|||
const auto infoLeft = infoRight - size.width();
|
||||
const auto infoTop = infoBottom - size.height();
|
||||
return _bottomInfo.textState(
|
||||
data(),
|
||||
this,
|
||||
point - QPoint{ infoLeft, infoTop });
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue