diff --git a/Telegram/Resources/icons/emoji/premium_lock.png b/Telegram/Resources/icons/emoji/premium_lock.png new file mode 100644 index 000000000..0498a400e Binary files /dev/null and b/Telegram/Resources/icons/emoji/premium_lock.png differ diff --git a/Telegram/Resources/icons/emoji/premium_lock@2x.png b/Telegram/Resources/icons/emoji/premium_lock@2x.png new file mode 100644 index 000000000..78390b734 Binary files /dev/null and b/Telegram/Resources/icons/emoji/premium_lock@2x.png differ diff --git a/Telegram/Resources/icons/emoji/premium_lock@3x.png b/Telegram/Resources/icons/emoji/premium_lock@3x.png new file mode 100644 index 000000000..81b8e0600 Binary files /dev/null and b/Telegram/Resources/icons/emoji/premium_lock@3x.png differ diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 0952f70f7..df0fa7ab0 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -345,7 +345,7 @@ void EditCaptionBox::setupEditEventHandler() { } return true; }; - const auto premium = _controller->session().user()->isPremium(); + const auto premium = _controller->session().premium(); auto list = Storage::PreparedFileFromFilesDialog( std::move(result), checkResult, @@ -524,7 +524,7 @@ void EditCaptionBox::updateEmojiPanelGeometry() { } bool EditCaptionBox::fileFromClipboard(not_null data) { - const auto premium = _controller->session().user()->isPremium(); + const auto premium = _controller->session().premium(); return setPreparedList(ListFromMimeData(data, premium)); } diff --git a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp index 031dd27b2..afdc9c194 100644 --- a/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp +++ b/Telegram/SourceFiles/boxes/filters/edit_filter_chats_list.cpp @@ -104,7 +104,7 @@ private: } [[nodiscard]] int Limit(not_null session) { - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); return Limit(session, (premium ? "dialog_filters_chats_limit_premium" diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp index 1b02125b3..5cf831940 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp @@ -454,7 +454,7 @@ void SimplePinsLimitBox( int limitDefault, const QString &keyPremium, int limitPremium) { - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); const auto defaultLimit = Limit(session, keyDefault, limitDefault); const auto premiumLimit = Limit(session, keyPremium, limitPremium); @@ -489,7 +489,7 @@ void SimplePinsLimitBox( void ChannelsLimitBox( not_null box, not_null session) { - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); const auto defaultLimit = Limit(session, "channels_limit_default", 500); const auto premiumLimit = Limit(session, "channels_limit_premium", 1000); @@ -574,7 +574,7 @@ void PublicLinksLimitBox( not_null box, not_null navigation) { const auto session = &navigation->session(); - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); const auto defaultLimit = Limit( session, @@ -639,7 +639,7 @@ void PublicLinksLimitBox( void FilterChatsLimitBox( not_null box, not_null session) { - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); const auto defaultLimit = Limit( session, @@ -679,7 +679,7 @@ void FilterChatsLimitBox( void FiltersLimitBox( not_null box, not_null session) { - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); const auto defaultLimit = Limit( session, @@ -754,7 +754,7 @@ void PinsLimitBox( void CaptionLimitBox( not_null box, not_null session) { - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); const auto defaultLimit = Limit( session, @@ -795,7 +795,7 @@ void CaptionLimitReachedBox( .text = tr::lng_caption_limit_reached(tr::now, lt_count, remove), .inform = true, }); - if (!session->user()->isPremium()) { + if (!session->premium()) { box->addLeftButton(tr::lng_limits_increase(), [=] { box->getDelegate()->showBox( Box(CaptionLimitBox, session), @@ -821,7 +821,7 @@ int CurrentPremiumLimit( int limitDefault, const QString &keyPremium, int limitPremium) { - const auto premium = session->user()->isPremium(); + const auto premium = session->premium(); return AppConfigLimit( session, premium ? keyPremium : keyDefault, diff --git a/Telegram/SourceFiles/boxes/reactions_settings_box.cpp b/Telegram/SourceFiles/boxes/reactions_settings_box.cpp index bd0207b30..22e28a108 100644 --- a/Telegram/SourceFiles/boxes/reactions_settings_box.cpp +++ b/Telegram/SourceFiles/boxes/reactions_settings_box.cpp @@ -447,7 +447,7 @@ void ReactionsSettingsBox( &button->lifetime()); button->setClickedCallback([=, emoji = r.emoji] { - if (premium && !controller->session().user()->isPremium()) { + if (premium && !controller->session().premium()) { Settings::ShowPremium(&controller->session()); return; } diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 78c3cc6bc..22d901734 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -404,7 +404,7 @@ void SendFilesBox::openDialogToAddFileToAlbum() { return true; }; const auto callback = [=](FileDialog::OpenResult &&result) { - const auto premium = _controller->session().user()->isPremium(); + const auto premium = _controller->session().premium(); FileDialogCallback( std::move(result), checkResult, @@ -575,7 +575,7 @@ void SendFilesBox::pushBlock(int from, int till) { return true; }; const auto callback = [=](FileDialog::OpenResult &&result) { - const auto premium = _controller->session().user()->isPremium(); + const auto premium = _controller->session().premium(); FileDialogCallback( std::move(result), checkResult, @@ -773,7 +773,7 @@ bool SendFilesBox::canAddFiles(not_null data) const { } bool SendFilesBox::addFiles(not_null data) { - const auto premium = _controller->session().user()->isPremium(); + const auto premium = _controller->session().premium(); auto list = [&] { const auto urls = data->hasUrls() ? data->urls() : QList(); auto result = CanAddUrls(urls) diff --git a/Telegram/SourceFiles/boxes/sticker_preview_box.cpp b/Telegram/SourceFiles/boxes/sticker_preview_box.cpp index 43c23baf9..c4a3ff33b 100644 --- a/Telegram/SourceFiles/boxes/sticker_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_preview_box.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "ui/chat/chat_theme.h" #include "ui/layers/generic_box.h" +#include "ui/effects/premium_graphics.h" #include "ui/widgets/buttons.h" #include "ui/widgets/gradient_round_button.h" #include "ui/wrap/padding_wrap.h" @@ -149,11 +150,7 @@ void PreloadSticker(const std::shared_ptr &media) { [[nodiscard]] object_ptr CreatePremiumButton( QWidget *parent) { - return CreateGradientButton(parent, { - { 0., st::premiumButtonBg1->c }, - { 0.6, st::premiumButtonBg2->c }, - { 1., st::premiumButtonBg3->c }, - }); + return CreateGradientButton(parent, Ui::Premium::ButtonGradientStops()); } [[nodiscard]] object_ptr CreateUnlockButton( @@ -167,6 +164,7 @@ void PreloadSticker(const std::shared_ptr &media) { result.data(), tr::lng_sticker_premium_button(), st::premiumPreviewButtonLabel); + label->setAttribute(Qt::WA_TransparentForMouseEvents); rpl::combine( result->widthValue(), label->widthValue() diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 98a78b043..fecbfcb7b 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -298,3 +298,5 @@ premiumPreviewButtonLabel: FlatLabel(defaultFlatLabel) { textFg: premiumButtonFg; style: semiboldTextStyle; } + +stickersPremiumLock: icon{{ "emoji/premium_lock", premiumButtonFg }}; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 9a1d5986b..18de32b54 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_cloud_file.h" #include "data/data_changes.h" +#include "data/data_peer_values.h" #include "menu/menu_send.h" // SendMenu::FillSendMenu #include "chat_helpers/stickers_lottie.h" #include "ui/widgets/buttons.h" @@ -21,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/effects/ripple_animation.h" #include "ui/effects/path_shift_gradient.h" +#include "ui/effects/premium_graphics.h" #include "ui/image/image.h" #include "ui/cached_round_corners.h" #include "lottie/lottie_multi_player.h" @@ -56,6 +58,7 @@ constexpr auto kPreloadOfficialPages = 4; constexpr auto kOfficialLoadLimit = 40; constexpr auto kMinRepaintDelay = crl::time(33); constexpr auto kMinAfterScrollDelay = crl::time(33); +constexpr auto kPremiumLockedOpacity = 0.5; using Data::StickersSet; using Data::StickersPack; @@ -1176,6 +1179,17 @@ StickersListWidget::StickersListWidget( ) | rpl::skip(1) | rpl::map_to( TabbedSelector::Action::Update ) | rpl::start_to_stream(_choosingUpdated, lifetime()); + + style::PaletteChanged( + ) | rpl::start_with_next([=] { + _premiumLock = QImage(); + }, lifetime()); + + Data::AmPremiumValue( + &session() + ) | rpl::start_with_next([=](bool premium) { + refreshStickers(); + }, lifetime()); } Main::Session &StickersListWidget::session() const { @@ -2276,6 +2290,7 @@ void StickersListWidget::paintSticker( return; } + const auto locked = document->isPremiumSticker() && !session().premium(); const auto isLottie = document->sticker()->isLottie(); const auto isWebm = document->sticker()->isWebm(); if (isLottie @@ -2302,6 +2317,9 @@ void StickersListWidget::paintSticker( (_singleSize.width() - size.width()) / 2, (_singleSize.height() - size.height()) / 2); + if (locked) { + p.setOpacity(kPremiumLockedOpacity); + } if (sticker.lottie && sticker.lottie->ready()) { auto request = Lottie::FrameRequest(); request.box = boundingBoxSize() * cIntRetinaFactor(); @@ -2341,6 +2359,7 @@ void StickersListWidget::paintSticker( sticker.savedFrameFor = _singleSize; } } else { + p.setOpacity(1.); PaintStickerThumbnailPath( p, media.get(), @@ -2358,16 +2377,40 @@ void StickersListWidget::paintSticker( p.setOpacity(1.); } - if (document->isPremiumSticker()) { + if (locked) { + p.setOpacity(1.); + + validatePremiumLock(); + const auto factor = style::DevicePixelRatio(); const auto point = pos + QPoint( - _singleSize.width() - st::stickerPanDeleteIconBg.width(), - _singleSize.height() - st::stickerPanDeleteIconBg.height()); - st::stickerPanDeleteIconBg.paint(p, point, width()); - st::stickerPanDeleteIconFg.paint(p, point, width()); + _singleSize.width() - (_premiumLock.width() / factor), + _singleSize.height() - (_premiumLock.height() / factor)); + p.drawImage(point, _premiumLock); } } +void StickersListWidget::validatePremiumLock() { + if (!_premiumLock.isNull()) { + return; + } + const auto factor = style::DevicePixelRatio(); + const auto size = st::stickersPremiumLock.size(); + _premiumLock = QImage( + size * factor, + QImage::Format_ARGB32_Premultiplied); + _premiumLock.setDevicePixelRatio(factor); + auto p = QPainter(&_premiumLock); + auto gradient = QLinearGradient( + QPoint(0, size.height()), + QPoint(size.width(), 0)); + gradient.setStops(Ui::Premium::LockGradientStops()); + p.fillRect(QRect(QPoint(), size), gradient); + st::stickersPremiumLock.paint(p, 0, 0, size.width()); + p.end(); + _premiumLock = Images::Circle(std::move(_premiumLock)); +} + int StickersListWidget::stickersRight() const { return stickersLeft() + (_columnCount * _singleSize.width()); } @@ -2991,7 +3034,7 @@ bool StickersListWidget::appendSet( PrepareStickers((set->stickers.empty() && externalLayout) ? set->covers : set->stickers)); - if (!externalLayout && _premiumsIndex >= 0) { + if (!externalLayout && _premiumsIndex >= 0 && session().premium()) { for (const auto &sticker : to.back().stickers) { const auto document = sticker.document; if (document->isPremiumSticker()) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 3fcbee7b4..b7ca4d829 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -330,6 +330,7 @@ private: void addSearchRow(not_null set); void showPreview(); + void validatePremiumLock(); Ui::MessageSendingAnimationFrom messageSentAnimationInfo( int section, @@ -390,6 +391,8 @@ private: base::Timer _previewTimer; bool _previewShown = false; + QImage _premiumLock; + std::map> _searchCache; std::vector> _searchIndex; base::Timer _searchRequestTimer; diff --git a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp index 58912f32f..5e4774a5a 100644 --- a/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp +++ b/Telegram/SourceFiles/history/view/history_view_emoji_interactions.cpp @@ -94,18 +94,25 @@ void EmojiInteractions::play( void EmojiInteractions::playPremiumEffect( not_null view, Element *replacing) { + const auto already = ranges::contains(_plays, view, &Play::view); if (replacing) { const auto i = ranges::find(_plays, replacing, &Play::view); if (i != end(_plays)) { //if (i->premium) { // replacing->externalLottieProgressing(false); //} - i->view = view; + if (already) { + _plays.erase(i); + } else { + i->view = view; + } //if (i->premium) { // view->externalLottieProgressing(true); //} return; } + } else if (already) { + return; } if (const auto media = view->media()) { if (const auto document = media->getDocument()) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp index be086e6ef..10986eb7f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.cpp @@ -255,6 +255,7 @@ void Sticker::paintLottie( _lottieOncePlayed = true; _parent->delegate()->elementStartStickerLoop(_parent); } + checkPremiumEffectStart(); } bool Sticker::paintPixmap( @@ -362,7 +363,16 @@ void Sticker::refreshLink() { } }); } else if (sticker && sticker->set) { - _link = ShowSetHandler(_data); + if (_data->isPremiumSticker()) { + const auto weak = base::make_weak(this); + _link = std::make_shared([weak] { + if (const auto that = weak.get()) { + that->premiumStickerClicked(); + } + }); + } else { + _link = ShowSetHandler(_data); + } } else if (sticker && (_data->dimensions.width() > kStickerSideSize || _data->dimensions.height() > kStickerSideSize) @@ -388,6 +398,11 @@ void Sticker::emojiStickerClicked() { _parent->history()->owner().requestViewRepaint(_parent); } +void Sticker::premiumStickerClicked() { + _premiumEffectPlayed = false; + _parent->history()->owner().requestViewRepaint(_parent); +} + void Sticker::ensureDataMediaCreated() const { if (_dataMedia) { return; @@ -423,12 +438,15 @@ void Sticker::setupLottie() { ChatHelpers::StickerLottieSize::MessageHistory, size() * style::DevicePixelRatio(), Lottie::Quality::High); - if (_data->isPremiumSticker() - && !_premiumEffectPlayed) { + checkPremiumEffectStart(); + lottieCreated(); +} + +void Sticker::checkPremiumEffectStart() { + if (!_premiumEffectPlayed && _data->isPremiumSticker()) { _premiumEffectPlayed = true; _parent->delegate()->elementStartPremium(_parent, nullptr); } - lottieCreated(); } void Sticker::lottieCreated() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_sticker.h b/Telegram/SourceFiles/history/view/media/history_view_sticker.h index c0bcd67be..f8916c40e 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_sticker.h +++ b/Telegram/SourceFiles/history/view/media/history_view_sticker.h @@ -100,6 +100,8 @@ private: void lottieCreated(); void unloadLottie(); void emojiStickerClicked(); + void premiumStickerClicked(); + void checkPremiumEffectStart(); //bool markFramesTillExternal(); const not_null _parent; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 13fb3c78f..b6d2b8803 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -218,6 +218,10 @@ rpl::producer<> Session::downloaderTaskFinished() const { return downloader().taskFinished(); } +bool Session::premium() const { + return _user->isPremium(); +} + uint64 Session::uniqueId() const { // See also Account::willHaveSessionUniqueId. return userId().bare diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index c867e6917..2e6194f7b 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -80,6 +80,7 @@ public: [[nodiscard]] Domain &domain() const; [[nodiscard]] Storage::Domain &domainLocal() const; + [[nodiscard]] bool premium() const; [[nodiscard]] uint64 uniqueId() const; // userId() with TestDC shift. [[nodiscard]] UserId userId() const; [[nodiscard]] PeerId userPeerId() const; diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp index b3c16bd5a..4467f8f94 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.cpp @@ -29,15 +29,6 @@ constexpr auto kStepBeforeDeflection = 0.75; constexpr auto kStepAfterDeflection = kStepBeforeDeflection + (1. - kStepBeforeDeflection) / 2.; -[[nodiscard]] QGradientStops GradientStops() { - return QGradientStops{ - QGradientStop(0.0, st::premiumButtonBg1->c), - QGradientStop(.25, st::premiumButtonBg1->c), - QGradientStop(.85, st::premiumButtonBg2->c), - QGradientStop(1.0, st::premiumButtonBg3->c), - }; -} - [[nodiscard]] QLinearGradient ComputeGradient( not_null content, int left, @@ -46,7 +37,7 @@ constexpr auto kStepAfterDeflection = kStepBeforeDeflection // Take a full width of parent box without paddings. const auto fullGradientWidth = content->parentWidget()->width(); auto fullGradient = QLinearGradient(0, 0, fullGradientWidth, 0); - fullGradient.setStops(GradientStops()); + fullGradient.setStops(ButtonGradientStops()); auto gradient = QLinearGradient(0, 0, width, 0); const auto fullFinal = float64(fullGradient.finalStop().x()); @@ -506,5 +497,26 @@ void AddLimitRow(not_null parent, int max) { st::boxRowPadding); } +QGradientStops LimitGradientStops() { + return { + { 0.0, st::premiumButtonBg1->c }, + { .25, st::premiumButtonBg1->c }, + { .85, st::premiumButtonBg2->c }, + { 1.0, st::premiumButtonBg3->c }, + }; +} + +QGradientStops ButtonGradientStops() { + return { + { 0., st::premiumButtonBg1->c }, + { 0.6, st::premiumButtonBg2->c }, + { 1., st::premiumButtonBg3->c }, + }; +} + +QGradientStops LockGradientStops() { + return ButtonGradientStops(); +} + } // namespace Premium } // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/premium_graphics.h b/Telegram/SourceFiles/ui/effects/premium_graphics.h index b8c14d0ca..3c22ff059 100644 --- a/Telegram/SourceFiles/ui/effects/premium_graphics.h +++ b/Telegram/SourceFiles/ui/effects/premium_graphics.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include + namespace tr { template struct phrase; @@ -31,5 +33,9 @@ void AddBubbleRow( void AddLimitRow(not_null parent, int max); +[[nodiscard]] QGradientStops LimitGradientStops(); +[[nodiscard]] QGradientStops ButtonGradientStops(); +[[nodiscard]] QGradientStops LockGradientStops(); + } // namespace Premium } // namespace Ui