diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 108fb7d39..c0ef61a95 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -564,6 +564,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_effect_add_title" = "Add an animated effect"; "lng_effect_stickers_title" = "Effects from stickers"; "lng_effect_send" = "Send with Effect"; +"lng_effect_none" = "No effects found."; +"lng_effect_premium" = "Subscribe to {link} to add this animated effect."; +"lng_effect_premium_link" = "Telegram Premium"; "lng_languages" = "Languages"; "lng_languages_none" = "No languages found."; diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index efd034843..b3c589be7 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -73,7 +73,9 @@ using Data::StickersSet; using Data::StickersPack; using SetFlag = Data::StickersSetFlag; -[[nodiscard]] std::optional ComputeImageColor(const QImage &frame) { +[[nodiscard]] std::optional ComputeImageColor( + const style::icon &lockIcon, + const QImage &frame) { if (frame.isNull() || frame.format() != QImage::Format_ARGB32_Premultiplied) { return {}; @@ -83,7 +85,7 @@ using SetFlag = Data::StickersSetFlag; auto sb = int64(); auto sa = int64(); const auto factor = frame.devicePixelRatio(); - const auto size = st::stickersPremiumLock.size() * factor; + const auto size = lockIcon.size() * factor; const auto width = std::min(frame.width(), size.width()); const auto height = std::min(frame.height(), size.height()); const auto skipx = (frame.width() - width) / 2; @@ -110,22 +112,30 @@ using SetFlag = Data::StickersSetFlag; } -[[nodiscard]] QColor ComputeLockColor(const QImage &frame) { - return ComputeImageColor(frame).value_or(st::windowSubTextFg->c); +[[nodiscard]] QColor ComputeLockColor( + const style::icon &lockIcon, + const QImage &frame) { + return ComputeImageColor( + lockIcon, + frame + ).value_or(st::windowSubTextFg->c); } -void ValidatePremiumLockBg(QImage &image, const QImage &frame) { +void ValidatePremiumLockBg( + const style::icon &lockIcon, + QImage &image, + const QImage &frame) { if (!image.isNull()) { return; } const auto factor = style::DevicePixelRatio(); - const auto size = st::stickersPremiumLock.size(); + const auto size = lockIcon.size(); image = QImage( size * factor, QImage::Format_ARGB32_Premultiplied); image.setDevicePixelRatio(factor); auto p = QPainter(&image); - const auto color = ComputeLockColor(frame); + const auto color = ComputeLockColor(lockIcon, frame); p.fillRect( QRect(QPoint(), size), anim::color(color, st::windowSubTextFg, kGrayLockOpacity)); @@ -134,12 +144,12 @@ void ValidatePremiumLockBg(QImage &image, const QImage &frame) { image = Images::Circle(std::move(image)); } -void ValidatePremiumStarFg(QImage &image) { +void ValidatePremiumStarFg(const style::icon &lockIcon, QImage &image) { if (!image.isNull()) { return; } const auto factor = style::DevicePixelRatio(); - const auto size = st::stickersPremiumLock.size(); + const auto size = lockIcon.size(); image = QImage( size * factor, QImage::Format_ARGB32_Premultiplied); @@ -176,7 +186,10 @@ void ValidatePremiumStarFg(QImage &image) { } // namespace -StickerPremiumMark::StickerPremiumMark(not_null session) { +StickerPremiumMark::StickerPremiumMark( + not_null session, + const style::icon &lockIcon) +: _lockIcon(lockIcon) { style::PaletteChanged( ) | rpl::start_with_next([=] { _lockGray = QImage(); @@ -202,16 +215,14 @@ void StickerPremiumMark::paint( const auto factor = style::DevicePixelRatio(); const auto radius = st::roundRadiusSmall; const auto point = position + QPoint( - (_premium - ? (singleSize.width() - (bg.width() / factor) - radius) - : (singleSize.width() - (bg.width() / factor)) / 2), + (singleSize.width() - (bg.width() / factor) - radius), singleSize.height() - (bg.height() / factor) - radius); p.drawImage(point, bg); if (_premium) { validateStar(); p.drawImage(point, _star); } else { - st::stickersPremiumLock.paint(p, point, outerWidth); + _lockIcon.paint(p, point, outerWidth); } } @@ -219,11 +230,11 @@ void StickerPremiumMark::validateLock( const QImage &frame, QImage &backCache) { auto &image = frame.isNull() ? _lockGray : backCache; - ValidatePremiumLockBg(image, frame); + ValidatePremiumLockBg(_lockIcon, image, frame); } void StickerPremiumMark::validateStar() { - ValidatePremiumStarFg(_star); + ValidatePremiumStarFg(_lockIcon, _star); } class StickerSetBox::Inner final : public Ui::RpWidget { @@ -664,7 +675,7 @@ StickerSetBox::Inner::Inner( st::windowBgRipple, st::windowBgOver, [=] { repaintItems(); })) -, _premiumMark(_session) +, _premiumMark(_session, st::stickersPremiumLock) , _updateItemsTimer([=] { updateItems(); }) , _input(set) , _padding((type == Data::StickersType::Emoji) diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.h b/Telegram/SourceFiles/boxes/sticker_set_box.h index 72dd38320..e718cc45e 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.h +++ b/Telegram/SourceFiles/boxes/sticker_set_box.h @@ -30,7 +30,9 @@ class Show; class StickerPremiumMark final { public: - explicit StickerPremiumMark(not_null session); + StickerPremiumMark( + not_null session, + const style::icon &lockIcon); void paint( QPainter &p, @@ -44,6 +46,7 @@ private: void validateLock(const QImage &frame, QImage &backCache); void validateStar(); + const style::icon &_lockIcon; QImage _lockGray; QImage _star; bool _premium = false; diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 953232da9..d69d4e6bb 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -754,6 +754,7 @@ inlineResultsMinWidth: 48px; inlineDurationMargin: 3px; stickersPremiumLock: icon{{ "emoji/premium_lock", premiumButtonFg }}; +emojiPremiumLock: icon{{ "chat/mini_lock", premiumButtonFg }}; reactStripExtend: margins(21px, 49px, 39px, 0px); reactStripHeight: 40px; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 4962a42a9..c91ccf1d1 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -136,6 +136,7 @@ struct EmojiListWidget::CustomEmojiInstance { struct EmojiListWidget::RecentOne { Ui::Text::CustomEmoji *custom = nullptr; RecentEmojiId id; + mutable QImage premiumLock; }; EmojiColorPicker::EmojiColorPicker( @@ -478,8 +479,12 @@ EmojiListWidget::EmojiListWidget( , _localSetsManager( std::make_unique(&session())) , _customRecentFactory(std::move(descriptor.customRecentFactory)) +, _freeEffects(std::move(descriptor.freeEffects)) , _customTextColor(std::move(descriptor.customTextColor)) , _overBg(st::emojiPanRadius, st().overBg) +, _premiumMark(std::make_unique( + &session(), + st::emojiPremiumLock)) , _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg) , _picker(this, st()) , _showPickerTimer([=] { showPicker(); }) @@ -591,6 +596,10 @@ rpl::producer> EmojiListWidget::searchQueries() const { return _searchQueries.events(); } +rpl::producer EmojiListWidget::recentShownCount() const { + return _recentShownCount.value(); +} + void EmojiListWidget::applyNextSearchQuery() { if (_searchQuery == _nextSearchQuery) { return; @@ -612,6 +621,9 @@ void EmojiListWidget::applyNextSearchQuery() { _searchCustomIds.clear(); } resizeToWidth(width()); + _recentShownCount = searching + ? _searchResults.size() + : _recent.size(); update(); if (modeChanged) { visibleTopBottomUpdated(getVisibleTop(), getVisibleBottom()); @@ -1024,9 +1036,10 @@ int EmojiListWidget::countDesiredHeight(int newWidth) { const auto minimalLastHeight = std::max( minimalHeight - padding.bottom(), 0); - return qMax( - minimalHeight, - countResult(minimalLastHeight) + padding.bottom()); + const auto result = countResult(minimalLastHeight); + return result + ? qMax(minimalHeight, result + padding.bottom()) + : 0; } int EmojiListWidget::defaultMinimalHeight() const { @@ -1291,6 +1304,8 @@ void EmojiListWidget::paint( QRect clip) { validateEmojiPaintContext(context); + _paintAsPremium = session().premium(); + auto fromColumn = floorclamp( clip.x() - _rowsLeft, _singleSize.width(), @@ -1455,16 +1470,44 @@ void EmojiListWidget::drawRecent( QPoint position, const RecentOne &recent) { _recentPainted = true; + const auto locked = (_mode == Mode::MessageEffects) + && !_paintAsPremium + && v::is(recent.id.data) + && !_freeEffects.contains( + v::get(recent.id.data).id); + auto lockedPainted = false; + if (locked) { + if (_premiumMarkFrameCache.isNull()) { + const auto ratio = style::DevicePixelRatio(); + _premiumMarkFrameCache = QImage( + QSize(_customSingleSize, _customSingleSize) * ratio, + QImage::Format_ARGB32_Premultiplied); + _premiumMarkFrameCache.setDevicePixelRatio(ratio); + } + _premiumMarkFrameCache.fill(Qt::transparent); + } if (const auto custom = recent.custom) { - _emojiPaintContext->scale = context.progress; - _emojiPaintContext->position = position + const auto exactPosition = position + _innerPosition + _customPosition; + _emojiPaintContext->scale = context.progress; if (_mode == Mode::ChannelStatus) { _emojiPaintContext->internal.forceFirstFrame = (recent.id == _recent.front().id); } - custom->paint(p, *_emojiPaintContext); + if (locked) { + lockedPainted = custom->ready(); + + auto q = Painter(&_premiumMarkFrameCache); + _emojiPaintContext->position = QPoint(); + custom->paint(q, *_emojiPaintContext); + q.end(); + + p.drawImage(exactPosition, _premiumMarkFrameCache); + } else { + _emojiPaintContext->position = exactPosition; + custom->paint(p, *_emojiPaintContext); + } } else if (const auto emoji = std::get_if(&recent.id.data)) { if (_mode == Mode::EmojiStatus) { position += QPoint( @@ -1478,6 +1521,16 @@ void EmojiListWidget::drawRecent( } else { Unexpected("Empty custom emoji in EmojiListWidget::drawRecent."); } + + if (locked) { + _premiumMark->paint( + p, + lockedPainted ? _premiumMarkFrameCache : QImage(), + recent.premiumLock, + position, + _singleSize, + width()); + } } void EmojiListWidget::drawEmoji( diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 59bfbfce6..3aef95ac0 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/round_rect.h" #include "base/timer.h" +class StickerPremiumMark; + namespace style { struct EmojiPan; } // namespace style @@ -89,6 +91,7 @@ struct EmojiListDescriptor { Fn( DocumentId, Fn)> customRecentFactory; + base::flat_set freeEffects; const style::EmojiPan *st = nullptr; ComposeFeatures features; }; @@ -148,6 +151,7 @@ public: const SendMenu::Details &details) override; [[nodiscard]] rpl::producer> searchQueries() const; + [[nodiscard]] rpl::producer recentShownCount() const; protected: void visibleTopBottomUpdated( @@ -400,10 +404,13 @@ private: int _counts[kEmojiSectionCount]; std::vector _recent; base::flat_set _recentCustomIds; + base::flat_set _freeEffects; base::flat_set _repaintsScheduled; + rpl::variable _recentShownCount; std::unique_ptr _emojiPaintContext; bool _recentPainted = false; bool _grabbingChosen = false; + bool _paintAsPremium = false; QVector _emoji[kEmojiSectionCount]; std::vector _custom; base::flat_set _restrictedCustomList; @@ -417,6 +424,8 @@ private: Ui::RoundRect _overBg; QImage _searchExpandCache; + std::unique_ptr _premiumMark; + QImage _premiumMarkFrameCache; mutable std::unique_ptr _colorAllRipple; bool _colorAllRippleForced = false; rpl::lifetime _colorAllRippleForcedLifetime; diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 90f11e978..bece81b64 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -891,7 +891,7 @@ FieldAutocomplete::Inner::Inner( _st.pathBg, _st.pathFg, [=] { update(); })) -, _premiumMark(_session) +, _premiumMark(_session, st::stickersPremiumLock) , _previewTimer([=] { showPreview(); }) { _session->downloaderTaskFinished( ) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 0c46508e6..bb1278fd8 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -219,7 +219,9 @@ StickersListWidget::StickersListWidget( , _installedWidth(st::stickersTrendingInstalled.font->width(_installedText)) , _settings(this, tr::lng_stickers_you_have(tr::now)) , _previewTimer([=] { showPreview(); }) -, _premiumMark(std::make_unique(&session())) +, _premiumMark(std::make_unique( + &session(), + st::stickersPremiumLock)) , _searchRequestTimer([=] { sendSearchRequest(); }) { setMouseTracking(true); if (st().bg->c.alpha() > 0) { @@ -542,8 +544,8 @@ int StickersListWidget::countDesiredHeight(int newWidth) { const auto minimalLastHeight = (_section == Section::Stickers) ? minimalHeight : 0; - return qMax(minimalHeight, countResult(minimalLastHeight)) - + st::stickerPanPadding; + const auto result = qMax(minimalHeight, countResult(minimalLastHeight)); + return result ? (result + st::stickerPanPadding) : 0; } void StickersListWidget::sendSearchRequest() { @@ -675,9 +677,14 @@ void StickersListWidget::refreshSearchRows( _lastMousePosition = QCursor::pos(); resizeToWidth(width()); + _recentShownCount = _filteredStickers.size(); updateSelected(); } +rpl::producer StickersListWidget::recentShownCount() const { + return _recentShownCount.value(); +} + void StickersListWidget::fillLocalSearchRows(const QString &query) { const auto searchWordsList = TextUtilities::PrepareSearchWords(query); if (searchWordsList.isEmpty()) { @@ -910,6 +917,7 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { toColumn = _columnCount - toColumn; } + _paintAsPremium = session().premium(); _pathGradient->startFrame(0, width(), width() / 2); auto &sets = shownSets(); @@ -1489,7 +1497,7 @@ void StickersListWidget::paintSticker( : (set.id == SearchEmojiSectionSetId()) ? &_filterStickersCornerEmoji : nullptr; - if (corner && !corner->empty()) { + if (corner && !corner->empty() && _paintAsPremium) { Assert(index < corner->size()); if (const auto emoji = (*corner)[index]) { const auto size = Ui::Emoji::GetSizeNormal(); @@ -1960,6 +1968,11 @@ void StickersListWidget::setSection(Section section) { } clearHeavyData(); _section = section; + _recentShownCount = (section == Section::Search) + ? _filteredStickers.size() + : _mySets.empty() + ? 0 + : _mySets.front().stickers.size(); } void StickersListWidget::clearHeavyData() { @@ -2242,6 +2255,9 @@ void StickersListWidget::refreshRecentStickers(bool performResize) { clearSelection(); auto recentPack = collectRecentStickers(); + if (_section == Section::Stickers) { + _recentShownCount = recentPack.size(); + } auto recentIt = std::find_if(_mySets.begin(), _mySets.end(), [](auto &set) { return set.id == Data::Stickers::RecentSetId; }); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 1f1de231b..f1a89b210 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -128,6 +128,7 @@ public: bool mySetsEmpty() const; void applySearchQuery(std::vector &&query); + [[nodiscard]] rpl::producer recentShownCount() const; ~StickersListWidget(); @@ -388,6 +389,7 @@ private: base::flat_set> _favedStickersMap; std::weak_ptr _lottieRenderer; + bool _paintAsPremium = false; bool _showingSetById = false; crl::time _lastScrolledAt = 0; crl::time _lastFullUpdatedAt = 0; @@ -437,6 +439,7 @@ private: std::vector> _filteredStickers; std::vector _filterStickersCornerEmoji; + rpl::variable _recentShownCount; std::map> _searchCache; std::vector> _searchIndex; base::Timer _searchRequestTimer; diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp index 2f55b6048..7173d3aed 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -972,6 +972,7 @@ void Selector::createList() { : st::reactPanelScrollRounded); _scroll->hide(); + const auto effects = !_reactions.stickers.empty(); const auto st = lifetime().make_state(_st); st->padding.setTop(_skipy); if (!_reactions.customAllowed) { @@ -979,15 +980,35 @@ void Selector::createList() { } auto lists = _scroll->setOwnedWidget( object_ptr(_scroll)); + auto recentList = _strip + ? _unifiedFactoryOwner->unifiedIdsList() + : _recent; + auto freeEffects = base::flat_set(); + if (effects) { + auto free = base::flat_set(); + free.reserve(_reactions.recent.size()); + for (const auto &reaction : _reactions.recent) { + if (!reaction.premium) { + free.emplace(reaction.id); + } + } + for (const auto &id : recentList) { + const auto reactionId = _strip + ? _unifiedFactoryOwner->lookupReactionId(id) + : Data::ReactionId{ id }; + if (free.contains(reactionId)) { + freeEffects.insert(id); + } + } + } _list = lists->add( object_ptr(lists, EmojiListDescriptor{ .show = _show, .mode = _listMode, .paused = [] { return false; }, - .customRecentList = (_strip - ? _unifiedFactoryOwner->unifiedIdsList() - : _recent), + .customRecentList = std::move(recentList), .customRecentFactory = _unifiedFactoryOwner->factory(), + .freeEffects = std::move(freeEffects), .st = st, })); if (!_reactions.stickers.empty()) { @@ -1092,6 +1113,27 @@ void Selector::createList() { ) | rpl::start_with_next([=](std::vector &&query) { _stickers->applySearchQuery(std::move(query)); }, _stickers->lifetime()); + + + rpl::combine( + _list->recentShownCount(), + _stickers->recentShownCount() + ) | rpl::start_with_next([=](int emoji, int stickers) { + _showEmptySearch = !emoji && !stickers; + _scroll->update(); + }, _scroll->lifetime()); + + _scroll->paintRequest() | rpl::filter([=] { + return _showEmptySearch; + }) | rpl::start_with_next([=] { + auto p = QPainter(_scroll); + p.setPen(st::windowSubTextFg); + p.setFont(st::normalFont); + p.drawText( + _scroll->rect(), + tr::lng_effect_none(tr::now), + style::al_center); + }, _scroll->lifetime()); } else { _list->setMinimalHeight(geometry.width(), _scroll->height()); } diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h index ad173385d..d5b1e05d5 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h @@ -205,6 +205,7 @@ private: Ui::PlainShadow *_shadow = nullptr; rpl::variable _shadowTop = 0; rpl::variable _shadowSkip = 0; + bool _showEmptySearch = false; QImage _paintBuffer; Ui::Animations::Simple _expanding; diff --git a/Telegram/SourceFiles/menu/menu_send.cpp b/Telegram/SourceFiles/menu/menu_send.cpp index 3cfc57d0b..b56491bcb 100644 --- a/Telegram/SourceFiles/menu/menu_send.cpp +++ b/Telegram/SourceFiles/menu/menu_send.cpp @@ -29,9 +29,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/chat_theme.h" #include "ui/effects/path_shift_gradient.h" #include "ui/effects/ripple_animation.h" +#include "ui/text/text_utilities.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/shadow.h" +#include "ui/wrap/padding_wrap.h" #include "ui/painter.h" #include "data/data_document.h" #include "data/data_document_media.h" @@ -42,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "main/main_session.h" #include "apiwrap.h" +#include "settings/settings_premium.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" #include "styles/style_chat.h" @@ -89,6 +93,8 @@ private: void paintEvent(QPaintEvent *e) override; void mousePressEvent(QMouseEvent *e) override; + [[nodiscard]] bool canSend() const; + void setupGeometry(QPoint position); void setupBackground(); void setupItem(); @@ -110,6 +116,9 @@ private: const AdminLog::OwnedItem _replyTo; const AdminLog::OwnedItem _item; const std::unique_ptr _send; + const std::unique_ptr> _premiumPromoLabel; + const not_null _bottom; + const Fn _close; const Fn _actionWithEffect; QImage _icon; @@ -236,11 +245,26 @@ EffectPreview::EffectPreview( _replyTo->data()->fullId(), tr::lng_settings_chat_message_reply(tr::now), _effectId)) -, _send( - std::make_unique( +, _send(canSend() + ? std::make_unique( this, tr::lng_effect_send(tr::now), - st::effectPreviewSend)) + st::effectPreviewSend) + : nullptr) +, _premiumPromoLabel(canSend() + ? nullptr + : std::make_unique>( + this, + object_ptr( + this, + tr::lng_effect_premium( + lt_link, + tr::lng_effect_premium_link() | Ui::Text::ToLink(), + Ui::Text::WithEntities), + st::effectPreviewPromoLabel), + st::effectPreviewPromoPadding)) +, _bottom(_send ? ((Ui::RpWidget*)_send.get()) : _premiumPromoLabel.get()) +, _close(done) , _actionWithEffect(ComposeActionWithEffect(action, _effectId, done)) { setupGeometry(position); setupBackground(); @@ -291,8 +315,9 @@ void EffectPreview::setupGeometry(QPoint position) { const auto shadow = st::previewMenu.shadow; const auto extend = shadow.extend; _inner = QRect(QPoint(extend.left(), extend.top()), innerSize); + _bottom->resizeToWidth(_inner.width()); const auto size = _inner.marginsAdded(extend).size() - + QSize(0, _send->height()); + + QSize(0, _bottom->height()); const auto left = std::max( std::min( position.x() - size.width() / 2, @@ -305,11 +330,11 @@ void EffectPreview::setupGeometry(QPoint position) { parent->height() - size.height()), topMin); setGeometry(left, top, size.width(), size.height()); - _send->setGeometry( + _bottom->setGeometry( _inner.x(), _inner.y() + _inner.height(), _inner.width(), - _send->height()); + _bottom->height()); } void EffectPreview::setupBackground() { @@ -343,7 +368,7 @@ void EffectPreview::setupItem() { void EffectPreview::repaintBackground() { const auto ratio = style::DevicePixelRatio(); - const auto inner = _inner.size() + QSize(0, _send->height()); + const auto inner = _inner.size() + QSize(0, _bottom->height()); auto bg = QImage( inner * ratio, QImage::Format_ARGB32_Premultiplied); @@ -357,7 +382,7 @@ void EffectPreview::repaintBackground() { QSize(inner.width(), inner.height() * 5), QRect(QPoint(), inner)); p.fillRect( - QRect(0, _inner.height(), _inner.width(), _send->height()), + QRect(0, _inner.height(), _inner.width(), _bottom->height()), st::previewMarkRead.bgColor); auto hq = PainterHighQualityEnabler(p); p.setCompositionMode(QPainter::CompositionMode_DestinationIn); @@ -409,14 +434,32 @@ void EffectPreview::createLottie() { }, raw->lifetime()); } +bool EffectPreview::canSend() const { + return !_effect.premium || _show->session().premium(); +} + void EffectPreview::setupSend(Details details) { - _send->setClickedCallback([=] { - _actionWithEffect(Api::SendOptions(), details); - }); - const auto type = details.type; - SetupMenuAndShortcuts(_send.get(), _show, [=] { - return Details{ .type = type }; - }, _actionWithEffect); + if (_send) { + _send->setClickedCallback([=] { + _actionWithEffect(Api::SendOptions(), details); + }); + const auto type = details.type; + SetupMenuAndShortcuts(_send.get(), _show, [=] { + return Details{ .type = type }; + }, _actionWithEffect); + } else { + _premiumPromoLabel->entity()->setClickHandlerFilter([=](auto&&...) { + const auto window = _show->resolveWindow( + ChatHelpers::WindowUsage::PremiumPromo); + if (window) { + if (const auto onstack = _close) { + onstack(); + } + Settings::ShowPremium(window, "message_effect"); + } + return false; + }); + } } bool EffectPreview::checkReady() { diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index f194dc8aa..182861622 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1120,3 +1120,12 @@ effectPreviewSend: FlatButton(previewMarkRead) { bgColor: transparent; overBgColor: transparent; } +effectPreviewPromoLabel: FlatLabel(defaultFlatLabel) { + minWidth: 64px; + align: align(top); + textFg: windowSubTextFg; + style: TextStyle(defaultTextStyle) { + font: font(11px); + } +} +effectPreviewPromoPadding: margins(4px, 6px, 4px, 6px);