diff --git a/Telegram/SourceFiles/data/data_document_media.cpp b/Telegram/SourceFiles/data/data_document_media.cpp index 058b6b02f..83bf70d31 100644 --- a/Telegram/SourceFiles/data/data_document_media.cpp +++ b/Telegram/SourceFiles/data/data_document_media.cpp @@ -254,6 +254,7 @@ void DocumentMedia::videoThumbnailWanted(Data::FileOrigin origin) { void DocumentMedia::setVideoThumbnail(QByteArray content) { _videoThumbnailBytes = std::move(content); + _videoThumbnailBytes.detach(); } void DocumentMedia::checkStickerLarge() { diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 129bceb44..74d1e226d 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -414,6 +414,7 @@ not_null Session::processUser(const MTPUser &data) { | Flag::Scam | Flag::Fake | Flag::BotInlineGeo + | Flag::Premium | Flag::Support | (!minimal ? Flag::Contact @@ -2827,7 +2828,7 @@ void Session::documentApplyFields( ? Images::FromVideoSize(_session, data, *videoThumbnailSize) : ImageWithLocation(); const auto isPremiumSticker = videoThumbnailSize - && (videoThumbnailSize->c_videoSize().vtype().v == "fp"); + && (videoThumbnailSize->c_videoSize().vtype().v == "f"); documentApplyFields( document, data.vaccess_hash().v, diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 715985fb8..5a4ab88e7 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -673,6 +673,9 @@ void InnerWidget::elementReplyTo(const FullMsgId &to) { void InnerWidget::elementStartInteraction(not_null view) { } +void InnerWidget::elementStartPremium(not_null view) { +} + void InnerWidget::elementShowSpoilerAnimation() { _spoilerOpacity.stop(); _spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index f1f359e85..f51184a0c 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -138,6 +138,8 @@ public: void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction( not_null view) override; + void elementStartPremium( + not_null view) override; void elementShowSpoilerAnimation() override; ~InnerWidget(); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 6cfbba110..e982f5b71 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -254,6 +254,11 @@ public: _widget->elementStartInteraction(view); } } + void elementStartPremium(not_null view) override { + if (_widget) { + _widget->elementStartPremium(view); + } + } void elementShowSpoilerAnimation() override { if (_widget) { _widget->elementShowSpoilerAnimation(); @@ -3171,6 +3176,11 @@ void HistoryInner::elementStartInteraction(not_null view) { _controller->emojiInteractions().startOutgoing(view); } +void HistoryInner::elementStartPremium(not_null view) { + _emojiInteractions->playPremiumEffect(view); + _animatedStickersPlayed.emplace(view->data()); +} + void HistoryInner::elementShowSpoilerAnimation() { _spoilerOpacity.stop(); _spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration); diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 07b8fca9d..503bc1566 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -145,6 +145,7 @@ public: not_null elementPathShiftGradient(); void elementReplyTo(const FullMsgId &to); void elementStartInteraction(not_null view); + void elementStartPremium(not_null view); void elementShowSpoilerAnimation(); void updateBotInfo(bool recount = true); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index ea82ed344..64e7c2f86 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -205,6 +205,10 @@ void SimpleElementDelegate::elementStartInteraction( not_null view) { } +void SimpleElementDelegate::elementStartPremium( + not_null view) { +} + void SimpleElementDelegate::elementShowSpoilerAnimation() { } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 504a22f01..7b158446d 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -104,6 +104,7 @@ public: virtual not_null elementPathShiftGradient() = 0; virtual void elementReplyTo(const FullMsgId &to) = 0; virtual void elementStartInteraction(not_null view) = 0; + virtual void elementStartPremium(not_null view) = 0; virtual void elementShowSpoilerAnimation() = 0; virtual ~ElementDelegate() { @@ -162,6 +163,7 @@ public: not_null elementPathShiftGradient() override; void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction(not_null view) override; + void elementStartPremium(not_null view) override; void elementShowSpoilerAnimation() override; protected: diff --git a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp index b882c4952..95faad6e7 100644 --- a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp +++ b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp @@ -24,8 +24,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace HistoryView { namespace { -constexpr auto kSizeMultiplier = 3; -constexpr auto kCachesCount = 4; +constexpr auto kEmojiMultiplier = 3; +constexpr auto kPremiumMultiplier = 2.25; +constexpr auto kEmojiCachesCount = 4; +constexpr auto kPremiumCachesCount = 8; constexpr auto kMaxPlays = 5; constexpr auto kMaxPlaysWithSmallDelay = 3; constexpr auto kSmallDelay = crl::time(200); @@ -55,6 +57,7 @@ EmojiInteractions::EmojiInteractions(not_null session) }, _lifetime); _emojiSize = Sticker::EmojiSize(); + _premiumSize = Sticker::Size(); } EmojiInteractions::~EmojiInteractions() = default; @@ -84,27 +87,63 @@ void EmojiInteractions::play( } } +void EmojiInteractions::playPremiumEffect(not_null view) { + if (const auto media = view->media()) { + if (const auto document = media->getDocument()) { + if (document->isPremiumSticker()) { + play( + QString(), + view, + document, + document->createMediaView()->videoThumbnailContent(), + QString(), + false, + true); + } + } + } +} + void EmojiInteractions::play( QString emoticon, - not_null view, + not_null view, std::shared_ptr media, bool incoming) { + play( + std::move(emoticon), + view, + media->owner(), + media->bytes(), + media->owner()->filepath(), + incoming, + false); +} + +void EmojiInteractions::play( + QString emoticon, + not_null view, + not_null document, + QByteArray data, + QString filepath, + bool incoming, + bool premium) { const auto top = view->block()->y() + view->y(); const auto bottom = top + view->height(); if (_visibleTop >= bottom || _visibleBottom <= top - || _visibleTop == _visibleBottom) { + || _visibleTop == _visibleBottom + || (data.isEmpty() && filepath.isEmpty())) { return; } - auto lottie = preparePlayer(media.get()); + auto lottie = preparePlayer(document, data, filepath, premium); - const auto shift = GenerateRandomShift(_emojiSize); + const auto shift = premium ? QPoint() : GenerateRandomShift(_emojiSize); lottie->updates( ) | rpl::start_with_next([=](Lottie::Update update) { v::match(update.data, [&](const Lottie::Information &information) { }, [&](const Lottie::DisplayFrameRequest &request) { - const auto rect = computeRect(view).translated(shift); + const auto rect = computeRect(view, premium).translated(shift); if (rect.y() + rect.height() >= _visibleTop && rect.y() <= _visibleBottom) { _updateRequests.fire_copy(rect); @@ -115,19 +154,30 @@ void EmojiInteractions::play( .view = view, .lottie = std::move(lottie), .shift = shift, + .premium = premium, }); if (incoming) { _playStarted.fire(std::move(emoticon)); } if (const auto media = view->media()) { - media->stickerClearLoopPlayed(); + if (!premium) { + media->stickerClearLoopPlayed(); + } } } +QSize EmojiInteractions::sizeFor(bool premium) const { + return premium + ? (_premiumSize * kPremiumMultiplier) + : (_emojiSize * kEmojiMultiplier); +} + std::unique_ptr EmojiInteractions::preparePlayer( - not_null media) { + not_null document, + QByteArray data, + QString filepath, + bool premium) { // Shortened copy from stickers_lottie module. - const auto document = media->owner(); const auto baseKey = document->bigFileBaseCacheKey(); const auto tag = uint8(0); const auto keyShift = ((tag << 4) & 0xF0) @@ -149,10 +199,8 @@ std::unique_ptr EmojiInteractions::preparePlayer( std::move(data)); }); }; - const auto data = media->bytes(); - const auto filepath = document->filepath(); const auto request = Lottie::FrameRequest{ - _emojiSize * kSizeMultiplier * style::DevicePixelRatio(), + sizeFor(premium) * style::DevicePixelRatio(), }; auto &weakProvider = _sharedProviders[document]; auto shared = [&] { @@ -160,7 +208,7 @@ std::unique_ptr EmojiInteractions::preparePlayer( return result; } const auto result = Lottie::SinglePlayer::SharedProvider( - kCachesCount, + premium ? kPremiumCachesCount : kEmojiCachesCount, get, put, Lottie::ReadContent(data, filepath), @@ -179,19 +227,23 @@ void EmojiInteractions::visibleAreaUpdated( _visibleBottom = visibleBottom; } -QRect EmojiInteractions::computeRect(not_null view) const { +QRect EmojiInteractions::computeRect( + not_null view, + bool premium) const { const auto fullWidth = view->width(); - const auto shift = (_emojiSize.width() * kSizeMultiplier) / 40; + const auto sticker = premium ? _premiumSize : _emojiSize; + const auto size = sizeFor(premium); + const auto shift = size.width() / 40; const auto skip = (view->hasFromPhoto() ? st::msgPhotoSkip : 0) + st::msgMargin.left(); const auto rightAligned = view->hasOutLayout() && !view->delegate()->elementIsChatWide(); const auto left = rightAligned - ? (fullWidth - skip + shift - _emojiSize.width() * kSizeMultiplier) + ? (fullWidth - skip + shift - size.width()) : (skip - shift); const auto viewTop = view->block()->y() + view->y() + view->marginTop(); - const auto top = viewTop - _emojiSize.height(); - return QRect(QPoint(left, top), _emojiSize * kSizeMultiplier); + const auto top = viewTop + (sticker.height() - size.height()) / 2; + return QRect(QPoint(left, top), size); } void EmojiInteractions::paint(QPainter &p) { @@ -201,7 +253,7 @@ void EmojiInteractions::paint(QPainter &p) { continue; } auto request = Lottie::FrameRequest(); - request.box = _emojiSize * kSizeMultiplier * factor; + request.box = sizeFor(play.premium) * factor; const auto rightAligned = play.view->hasOutLayout() && !play.view->delegate()->elementIsChatWide(); if (!rightAligned) { @@ -217,7 +269,7 @@ void EmojiInteractions::paint(QPainter &p) { if (play.frame + 1 == play.framesCount) { play.finished = true; } - const auto rect = computeRect(play.view); + const auto rect = computeRect(play.view, play.premium); p.drawImage( QRect(rect.topLeft() + play.shift, frame.image.size() / factor), frame.image); diff --git a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h index 31b68cc97..3dd4f297a 100644 --- a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h +++ b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.h @@ -36,6 +36,7 @@ public: void play( ChatHelpers::EmojiInteractionPlayRequest request, not_null view); + void playPremiumEffect(not_null view); void visibleAreaUpdated(int visibleTop, int visibleBottom); void paint(QPainter &p); @@ -44,39 +45,55 @@ public: private: struct Play { - not_null view; + not_null view; std::unique_ptr lottie; QPoint shift; int frame = 0; int framesCount = 0; int frameRate = 0; + bool premium = false; bool finished = false; }; struct Delayed { QString emoticon; - not_null view; + not_null view; std::shared_ptr media; crl::time shouldHaveStartedAt = 0; bool incoming = false; }; - [[nodiscard]] QRect computeRect(not_null view) const; + [[nodiscard]] QRect computeRect( + not_null view, + bool premium) const; void play( QString emoticon, - not_null view, + not_null view, std::shared_ptr media, bool incoming); + void play( + QString emoticon, + not_null view, + not_null document, + QByteArray data, + QString filepath, + bool incoming, + bool premium); void checkDelayed(); + [[nodiscard]] QSize sizeFor(bool premium) const; [[nodiscard]] std::unique_ptr preparePlayer( - not_null media); + not_null document, + QByteArray data, + QString filepath, + bool premium); const not_null _session; int _visibleTop = 0; int _visibleBottom = 0; QSize _emojiSize; + QSize _premiumSize; std::vector _plays; std::vector _delayed; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 3daa4b389..d4de42223 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -1519,6 +1519,9 @@ void ListWidget::elementReplyTo(const FullMsgId &to) { void ListWidget::elementStartInteraction(not_null view) { } +void ListWidget::elementStartPremium(not_null view) { +} + void ListWidget::elementShowSpoilerAnimation() { _spoilerOpacity.stop(); _spoilerOpacity.start([=] { update(); }, 0., 1., st::fadeWrapDuration); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 7e086435e..6a697a9b8 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -292,6 +292,7 @@ public: not_null elementPathShiftGradient() override; void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction(not_null view) override; + void elementStartPremium(not_null view) override; void elementShowSpoilerAnimation() override; void setEmptyInfoWidget(base::unique_qptr &&w); diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index 130acf6de..ee0a3d3e1 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -68,6 +68,9 @@ Sticker::Sticker( dataMediaCreated(); } else { _data->loadThumbnail(parent->data()->fullId()); + if (_data->isPremiumSticker()) { + _data->loadVideoThumbnail(parent->data()->fullId()); + } } if (const auto media = replacing ? replacing->media() : nullptr) { _lottie = media->stickerTakeLottie(_data, _replacements); @@ -123,7 +126,9 @@ bool Sticker::readyToDrawLottie() { ensureDataMediaCreated(); _dataMedia->checkStickerLarge(); const auto loaded = _dataMedia->loaded(); - if (sticker->isLottie() && !_lottie && loaded) { + const auto waitingForPremium = _data->isPremiumSticker() + && _dataMedia->videoThumbnailContent().isEmpty(); + if (sticker->isLottie() && !_lottie && loaded && !waitingForPremium) { setupLottie(); } return (_lottie && _lottie->ready()); @@ -153,6 +158,19 @@ void Sticker::draw( } } +ClickHandlerPtr Sticker::link() { + return _link; +} + +DocumentData *Sticker::document() { + return _data; +} + +void Sticker::stickerClearLoopPlayed() { + _lottieOncePlayed = false; + _premiumEffectPlayed = false; +} + void Sticker::paintLottie( Painter &p, const PaintContext &context, @@ -162,6 +180,14 @@ void Sticker::paintLottie( if (context.selected() && !_nextLastDiceFrame) { request.colored = context.st->msgStickerOverlay()->c; } + const auto premium = _data->isPremiumSticker(); + if (premium) { + const auto rightAligned = _parent->hasOutLayout() + && !_parent->delegate()->elementIsChatWide(); + if (!rightAligned) { + request.mirrorHorizontal = true; + } + } const auto frame = _lottie ? _lottie->frameInfo(request) : Lottie::Animation::FrameInfo(); @@ -201,10 +227,10 @@ void Sticker::paintLottie( _framesCount = count; _nextLastDiceFrame = !paused && (_diceIndex > 0) - && (frame.index + 2 == count); + && (_frameIndex + 2 == count); const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd(); const auto switchToNext = !playOnce - || (!lastDiceFrame && (frame.index != 0 || !_lottieOncePlayed)); + || (!lastDiceFrame && (_frameIndex != 0 || !_lottieOncePlayed)); if (!paused && switchToNext && _lottie->markFrameShown() @@ -338,6 +364,9 @@ void Sticker::dataMediaCreated() const { if (_dataMedia->thumbnailPath().isEmpty()) { _dataMedia->thumbnailWanted(_parent->data()->fullId()); } + if (_data->isPremiumSticker()) { + _data->loadVideoThumbnail(_parent->data()->fullId()); + } _parent->history()->owner().registerHeavyViewPart(_parent); } @@ -355,6 +384,11 @@ void Sticker::setupLottie() { ChatHelpers::StickerLottieSize::MessageHistory, size() * cIntRetinaFactor(), Lottie::Quality::High); + if (_data->isPremiumSticker() + && !_premiumEffectPlayed) { + _premiumEffectPlayed = true; + _parent->delegate()->elementStartPremium(_parent); + } lottieCreated(); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.h b/Telegram/SourceFiles/history/view/media/history_view_sticker.h index 89720cbb5..d95252841 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.h +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.h @@ -43,16 +43,10 @@ public: Painter &p, const PaintContext &context, const QRect &r) override; - ClickHandlerPtr link() override { - return _link; - } + ClickHandlerPtr link() override; - DocumentData *document() override { - return _data; - } - void stickerClearLoopPlayed() override { - _lottieOncePlayed = false; - } + DocumentData *document() override; + void stickerClearLoopPlayed() override; std::unique_ptr stickerTakeLottie( not_null data, const Lottie::ColorReplacements *replacements) override; @@ -111,6 +105,7 @@ private: mutable int _frameIndex = -1; mutable int _framesCount = -1; mutable bool _lottieOncePlayed = false; + mutable bool _premiumEffectPlayed = false; mutable bool _nextLastDiceFrame = false; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/storage/serialize_document.cpp b/Telegram/SourceFiles/storage/serialize_document.cpp index ea944975a..9de41d38e 100644 --- a/Telegram/SourceFiles/storage/serialize_document.cpp +++ b/Telegram/SourceFiles/storage/serialize_document.cpp @@ -45,7 +45,9 @@ void Document::writeToStream(QDataStream &stream, DocumentData *document) { } } stream << qint32(document->getDuration()); - stream << qint32(document->isPremiumSticker() ? 1 : 0); + if (document->type == StickerDocument) { + stream << qint32(document->isPremiumSticker() ? 1 : 0); + } writeImageLocation(stream, document->thumbnailLocation()); stream << qint32(document->thumbnailByteSize()); writeImageLocation(stream, document->videoThumbnailLocation());