diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 87c7461b2..2114e914c 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_text_entities.h" #include "main/main_session.h" #include "data/data_peer_values.h" +#include "data/data_document.h" #include "data/data_session.h" #include "data/data_peer.h" #include "apiwrap.h" @@ -24,7 +25,7 @@ Premium::Premium(not_null api) // only queued, because it is not constructed yet. Data::AmPremiumValue( _session - ) | rpl::skip(1) | rpl::start_with_next([=] { + ) | rpl::start_with_next([=] { reload(); }, _session->lifetime()); }); @@ -35,6 +36,24 @@ rpl::producer Premium::statusTextValue() const { _statusText.value_or(TextWithEntities())); } +auto Premium::videos() const +-> const base::flat_map> & { + return _videos; +} + +rpl::producer<> Premium::videosUpdated() const { + return _videosUpdated.events(); +} + +auto Premium::stickers() const +-> const std::vector> & { + return _stickers; +} + +rpl::producer<> Premium::stickersUpdated() const { + return _stickersUpdated.events(); +} + int64 Premium::monthlyAmount() const { return _monthlyAmount; } @@ -44,12 +63,17 @@ QString Premium::monthlyCurrency() const { } void Premium::reload() { - if (_statusRequestId) { + reloadPromo(); + reloadStickers(); +} + +void Premium::reloadPromo() { + if (_promoRequestId) { return; } - _statusRequestId = _api.request(MTPhelp_GetPremiumPromo( + _promoRequestId = _api.request(MTPhelp_GetPremiumPromo( )).done([=](const MTPhelp_PremiumPromo &result) { - _statusRequestId = 0; + _promoRequestId = 0; result.match([&](const MTPDhelp_premiumPromo &data) { _session->data().processUsers(data.vusers()); _monthlyAmount = data.vmonthly_amount().v; @@ -60,9 +84,56 @@ void Premium::reload() { }; _statusText = text; _statusTextUpdates.fire(std::move(text)); + auto videos = base::flat_map>(); + const auto count = int(std::min( + data.vvideo_sections().v.size(), + data.vvideos().v.size())); + videos.reserve(count); + for (auto i = 0; i != count; ++i) { + const auto document = _session->data().processDocument( + data.vvideos().v[i]); + if ((!document->isVideoFile() && !document->isGifv()) + || !document->supportsStreaming()) { + document->forceIsStreamedAnimation(); + } + videos.emplace( + qs(data.vvideo_sections().v[i]), + document); + } + if (_videos != videos) { + _videos = std::move(videos); + _videosUpdated.fire({}); + } }); }).fail([=] { - _statusRequestId = 0; + _promoRequestId = 0; + }).send(); +} + +void Premium::reloadStickers() { + if (_stickersRequestId) { + return; + } + _stickersRequestId = _api.request(MTPmessages_GetStickers( + MTP_string("\xe2\xad\x90\xef\xb8\x8f\xe2\xad\x90\xef\xb8\x8f"), + MTP_long(_stickersHash) + )).done([=](const MTPmessages_Stickers &result) { + _stickersRequestId = 0; + result.match([&](const MTPDmessages_stickersNotModified &) { + }, [&](const MTPDmessages_stickers &data) { + _stickersHash = data.vhash().v; + const auto owner = &_session->data(); + _stickers.clear(); + for (const auto &sticker : data.vstickers().v) { + const auto document = owner->processDocument(sticker); + if (document->isPremiumSticker()) { + _stickers.push_back(document); + } + } + _stickersUpdated.fire({}); + }); + }).fail([=] { + _stickersRequestId = 0; }).send(); } diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index 921490cbc..b89d92b45 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -24,17 +24,36 @@ public: void reload(); [[nodiscard]] rpl::producer statusTextValue() const; + [[nodiscard]] auto videos() const + -> const base::flat_map> &; + [[nodiscard]] rpl::producer<> videosUpdated() const; + + [[nodiscard]] auto stickers() const + -> const std::vector> &; + [[nodiscard]] rpl::producer<> stickersUpdated() const; + [[nodiscard]] int64 monthlyAmount() const; [[nodiscard]] QString monthlyCurrency() const; private: + void reloadPromo(); + void reloadStickers(); + const not_null _session; MTP::Sender _api; - mtpRequestId _statusRequestId = 0; + mtpRequestId _promoRequestId = 0; std::optional _statusText; rpl::event_stream _statusTextUpdates; + base::flat_map> _videos; + rpl::event_stream<> _videosUpdated; + + mtpRequestId _stickersRequestId = 0; + uint64 _stickersHash = 0; + std::vector> _stickers; + rpl::event_stream<> _stickersUpdated; + int64 _monthlyAmount = 0; QString _monthlyCurrency; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 173f8682a..9a62d866f 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2454,6 +2454,8 @@ void ApiWrap::refreshFileReference( MTP_long(0))); }, [&](Data::FileOriginRingtones data) { request(MTPaccount_GetSavedRingtones(MTP_long(0))); + }, [&](Data::FileOriginPremiumPreviews data) { + request(MTPhelp_GetPremiumPromo()); }, [&](v::null_t) { fail(); }); diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp index d196c79cd..6686fcf7e 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_message_reactions.h" #include "data/data_document_media.h" +#include "data/data_streaming.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/chat/chat_theme.h" @@ -21,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/generic_box.h" #include "ui/effects/path_shift_gradient.h" #include "ui/effects/premium_graphics.h" +#include "ui/effects/gradient.h" #include "ui/text/text.h" #include "ui/text/text_utilities.h" #include "ui/widgets/buttons.h" @@ -31,7 +33,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lottie/lottie_single_player.h" #include "history/view/media/history_view_sticker.h" #include "history/view/history_view_element.h" +#include "media/streaming/media_streaming_instance.h" +#include "media/streaming/media_streaming_player.h" #include "window/window_session_controller.h" +#include "api/api_premium.h" +#include "apiwrap.h" #include "styles/style_layers.h" #include "styles/style_chat_helpers.h" @@ -41,6 +47,7 @@ constexpr auto kPremiumShift = 21. / 240; constexpr auto kShiftDuration = crl::time(200); constexpr auto kReactionsPerRow = 5; constexpr auto kDisabledOpacity = 0.5; +constexpr auto kPreviewsCount = int(PremiumPreview::kCount); struct Descriptor { PremiumPreview section = PremiumPreview::Stickers; @@ -92,6 +99,54 @@ void PreloadSticker(const std::shared_ptr &media) { media->videoThumbnailWanted(origin); } +[[nodiscard]] rpl::producer SectionTitle(PremiumPreview section) { + switch (section) { + case PremiumPreview::MoreUpload: + return tr::lng_premium_summary_subtitle_more_upload(); + case PremiumPreview::FasterDownload: + return tr::lng_premium_summary_subtitle_faster_download(); + case PremiumPreview::VoiceToText: + return tr::lng_premium_summary_subtitle_voice_to_text(); + case PremiumPreview::NoAds: + return tr::lng_premium_summary_subtitle_no_ads(); + case PremiumPreview::Reactions: + return tr::lng_premium_summary_subtitle_unique_reactions(); + case PremiumPreview::Stickers: + return tr::lng_premium_summary_subtitle_premium_stickers(); + case PremiumPreview::AdvancedChatManagement: + return tr::lng_premium_summary_subtitle_advanced_chat_management(); + case PremiumPreview::ProfileBadge: + return tr::lng_premium_summary_subtitle_profile_badge(); + case PremiumPreview::AnimatedUserpics: + return tr::lng_premium_summary_subtitle_animated_userpics(); + } + Unexpected("PremiumPreview in SectionTitle."); +} + +[[nodiscard]] rpl::producer SectionAbout(PremiumPreview section) { + switch (section) { + case PremiumPreview::MoreUpload: + return tr::lng_premium_summary_about_more_upload(); + case PremiumPreview::FasterDownload: + return tr::lng_premium_summary_about_faster_download(); + case PremiumPreview::VoiceToText: + return tr::lng_premium_summary_about_voice_to_text(); + case PremiumPreview::NoAds: + return tr::lng_premium_summary_about_no_ads(); + case PremiumPreview::Reactions: + return tr::lng_premium_summary_about_unique_reactions(); + case PremiumPreview::Stickers: + return tr::lng_premium_summary_about_premium_stickers(); + case PremiumPreview::AdvancedChatManagement: + return tr::lng_premium_summary_about_advanced_chat_management(); + case PremiumPreview::ProfileBadge: + return tr::lng_premium_summary_about_profile_badge(); + case PremiumPreview::AnimatedUserpics: + return tr::lng_premium_summary_about_animated_userpics(); + } + Unexpected("PremiumPreview in SectionTitle."); +} + [[nodiscard]] object_ptr ChatBackPreview( QWidget *parent, int height, @@ -113,10 +168,15 @@ void PreloadSticker(const std::shared_ptr &media) { not_null controller, const std::shared_ptr &media) { using namespace HistoryView; + + PreloadSticker(media); + const auto document = media->owner(); const auto lottieSize = Sticker::Size(document); const auto effectSize = Sticker::PremiumEffectSize(document); const auto result = Ui::CreateChild(parent.get()); + result->show(); + parent->sizeValue( ) | rpl::start_with_next([=](QSize size) { result->setGeometry(QRect( @@ -218,6 +278,196 @@ void PreloadSticker(const std::shared_ptr &media) { return result; } +[[nodiscard]] not_null StickersPreview( + not_null parent, + not_null controller) { + const auto result = Ui::CreateChild(parent.get()); + result->show(); + + parent->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + result->setGeometry(QRect(QPoint(), size)); + }, result->lifetime()); + auto &lifetime = result->lifetime(); + + struct State { + std::vector> medias; + Ui::RpWidget *single = nullptr; + }; + const auto premium = &controller->session().api().premium(); + const auto state = lifetime.make_state(); + const auto fill = [=] { + const auto &list = premium->stickers(); + for (const auto &document : list) { + state->medias.push_back(document->createMediaView()); + } + if (state->medias.empty()) { + return; + } + state->single = StickerPreview( + result, + controller, + state->medias.front()); + }; + fill(); + if (state->medias.empty()) { + premium->stickersUpdated( + ) | rpl::take(1) | rpl::start_with_next(fill, lifetime); + } + + return result; +} + +[[nodiscard]] DocumentData *LookupVideo( + not_null session, + PremiumPreview section) { + const auto name = [&] { + switch (section) { + case PremiumPreview::MoreUpload: return "more_upload"; + case PremiumPreview::FasterDownload: return "faster_download"; + case PremiumPreview::VoiceToText: return "voice_to_text"; + case PremiumPreview::NoAds: return "no_ads"; + case PremiumPreview::AdvancedChatManagement: + return "advanced_chat_management"; + case PremiumPreview::ProfileBadge: return "profile_badge"; + case PremiumPreview::AnimatedUserpics: return "animated_userpics"; + } + return ""; + }(); + const auto &videos = session->api().premium().videos(); + const auto i = videos.find(name); + return (i != end(videos)) ? i->second.get() : nullptr; +} + +[[nodiscard]] not_null VideoPreview( + not_null parent, + not_null controller, + not_null document) { + const auto result = Ui::CreateChild(parent.get()); + result->show(); + + parent->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + result->setGeometry(parent->rect()); + }, result->lifetime()); + auto &lifetime = result->lifetime(); + + auto shared = document->owner().streaming().sharedDocument( + document, + Data::FileOriginPremiumPreviews()); + if (!shared) { + return result; + } + + struct State { + State( + std::shared_ptr shared, + Fn waitingCallback) + : instance(shared, std::move(waitingCallback)) { + } + QImage blurred; + Media::Streaming::Instance instance; + std::shared_ptr media; + }; + const auto state = lifetime.make_state(std::move(shared), [] {}); + state->media = document->createMediaView(); + if (const auto image = state->media->thumbnailInline()) { + state->blurred = image->original(); + } + const auto width = st::premiumVideoWidth; + const auto height = state->blurred.size().isEmpty() + ? width + : int(base::SafeRound(float64(width) * state->blurred.height() + / state->blurred.width())); + if (!state->blurred.isNull()) { + const auto factor = style::DevicePixelRatio(); + state->blurred = state->blurred.scaled( + QSize(width, height) * factor, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation); + } + const auto check = [=] { + if (state->instance.playerLocked()) { + return; + } else if (state->instance.paused()) { + state->instance.resume(); + } + if (!state->instance.active() && !state->instance.failed()) { + auto options = Media::Streaming::PlaybackOptions(); + options.waitForMarkAsShown = true; + options.mode = ::Media::Streaming::Mode::Video; + options.loop = true; + state->instance.play(options); + } + }; + state->instance.player().updates( + ) | rpl::start_with_next_error([=](Media::Streaming::Update &&update) { + if (v::is(update.data) + || v::is(update.data)) { + result->update(); + } + }, [=](::Media::Streaming::Error &&error) { + result->update(); + }, state->instance.lifetime()); + + result->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(result); + + check(); + const auto left = (result->width() - width) / 2; + const auto top = (result->height() - height); + const auto ready = state->instance.player().ready() + && !state->instance.player().videoSize().isEmpty(); + const auto size = QSize(width, height) * style::DevicePixelRatio(); + const auto frame = !ready + ? state->blurred + : state->instance.frame({ .resize = size, .outer = size }); + p.drawImage(QRect(left, top, width, height), frame); + if (ready) { + state->instance.markFrameShown(); + } + }, lifetime); + + return result; +} + +[[nodiscard]] not_null GenericPreview( + not_null parent, + not_null controller, + PremiumPreview section) { + const auto result = Ui::CreateChild(parent.get()); + result->show(); + + parent->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + result->setGeometry(QRect(QPoint(), size)); + }, result->lifetime()); + auto &lifetime = result->lifetime(); + + struct State { + std::vector> medias; + Ui::RpWidget *single = nullptr; + }; + const auto session = &controller->session(); + const auto state = lifetime.make_state(); + const auto create = [=] { + const auto document = LookupVideo(session, section); + if (!document) { + return; + } + state->single = VideoPreview(result, controller, document); + }; + create(); + if (!state->single) { + session->api().premium().videosUpdated( + ) | rpl::take(1) | rpl::start_with_next(create, lifetime); + } + + return result; +} + + class ReactionPreview final { public: ReactionPreview( @@ -467,6 +717,8 @@ void ReactionPreview::paintEffect(QPainter &p) { int selected = -1; }; const auto result = Ui::CreateChild(parent.get()); + result->show(); + auto &lifetime = result->lifetime(); const auto state = lifetime.make_state(); @@ -605,6 +857,20 @@ void ReactionPreview::paintEffect(QPainter &p) { return result; } +[[nodiscard]] not_null GenerateDefaultPreview( + not_null parent, + not_null controller, + PremiumPreview section) { + switch (section) { + case PremiumPreview::Reactions: + return ReactionsPreview(parent, controller, {}); + case PremiumPreview::Stickers: + return StickersPreview(parent, controller); + default: + return GenericPreview(parent, controller, section); + } +} + [[nodiscard]] object_ptr CreateGradientButton( QWidget *parent, QGradientStops stops) { @@ -641,7 +907,49 @@ void ReactionPreview::paintEffect(QPainter &p) { return result; } -void StickerBox( +[[nodiscard]] object_ptr CreateSwitch( + not_null parent, + not_null*> selected) { + const auto padding = st::premiumDotPadding; + const auto width = padding.left() + st::premiumDot + padding.right(); + const auto height = padding.top() + st::premiumDot + padding.bottom(); + const auto stops = Ui::Premium::ButtonGradientStops(); + auto result = object_ptr(parent.get(), height); + const auto raw = result.data(); + for (auto i = 0; i != kPreviewsCount; ++i) { + const auto section = PremiumPreview(i); + const auto button = Ui::CreateChild(raw); + parent->widthValue( + ) | rpl::start_with_next([=](int outer) { + const auto full = width * kPreviewsCount; + const auto left = (outer - full) / 2 + (i * width); + button->setGeometry(left, 0, width, height); + }, button->lifetime()); + button->setClickedCallback([=] { + *selected = section; + }); + button->paintRequest( + ) | rpl::start_with_next([=] { + auto p = QPainter(button); + auto hq = PainterHighQualityEnabler(p); + p.setBrush((selected->current() == section) + ? anim::gradient_color_at( + stops, + float64(i) / (kPreviewsCount - 1)) + : st::windowBgRipple->c); + p.setPen(Qt::NoPen); + p.drawEllipse( + button->rect().marginsRemoved(st::premiumDotPadding)); + }, button->lifetime()); + selected->changes( + ) | rpl::start_with_next([=] { + button->update(); + }, button->lifetime()); + } + return result; +} + +void PreviewBox( not_null box, not_null controller, const Descriptor &descriptor, @@ -656,30 +964,46 @@ void StickerBox( {}); struct State { Ui::RpWidget *content = nullptr; + rpl::variable selected; }; const auto state = outer->lifetime().make_state(); + state->selected = descriptor.section; - auto text = rpl::producer(); - auto title = rpl::producer(); switch (descriptor.section) { case PremiumPreview::Stickers: Assert(media != nullptr); state->content = StickerPreview(outer, controller, media); - text = tr::lng_premium_summary_about_premium_stickers(); - title = tr::lng_premium_summary_subtitle_premium_stickers(); break; case PremiumPreview::Reactions: state->content = ReactionsPreview( outer, controller, descriptor.disabled); - text = tr::lng_premium_summary_about_unique_reactions(); - title = tr::lng_premium_summary_subtitle_unique_reactions(); break; - case PremiumPreview::Avatars: + default: + state->content = GenericPreview( + outer, + controller, + descriptor.section); break; } + state->selected.changes( + ) | rpl::start_with_next([=](PremiumPreview section) { + delete base::take(state->content); + state->content = GenerateDefaultPreview(outer, controller, section); + }, outer->lifetime()); + + auto title = state->selected.value( + ) | rpl::map([=](PremiumPreview section) { + return SectionTitle(section); + }) | rpl::flatten_latest(); + + auto text = state->selected.value( + ) | rpl::map([=](PremiumPreview section) { + return SectionAbout(section); + }) | rpl::flatten_latest(); + const auto padding = st::premiumPreviewAboutPadding; const auto available = size.width() - padding.left() - padding.right(); auto titleLabel = object_ptr( @@ -700,6 +1024,9 @@ void StickerBox( box->addRow( object_ptr>(box, std::move(textLabel)), padding); + box->addRow( + CreateSwitch(box->verticalLayout(), &state->selected), + st::premiumDotsMargin); box->setStyle(st::premiumPreviewBox); const auto buttonPadding = st::premiumPreviewBox.buttonPadding; const auto width = size.width() @@ -717,7 +1044,7 @@ void Show( const Descriptor &descriptor, const std::shared_ptr &media, QImage back) { - controller->show(Box(StickerBox, controller, descriptor, media, back)); + controller->show(Box(PreviewBox, controller, descriptor, media, back)); } void Show(not_null controller, QImage back) { diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.h b/Telegram/SourceFiles/boxes/premium_preview_box.h index 01513f931..f6fe1ec1d 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.h +++ b/Telegram/SourceFiles/boxes/premium_preview_box.h @@ -22,9 +22,17 @@ void ShowStickerPreviewBox( not_null document); enum class PremiumPreview { + MoreUpload, + FasterDownload, + VoiceToText, + NoAds, Reactions, Stickers, - Avatars, + AdvancedChatManagement, + ProfileBadge, + AnimatedUserpics, + + kCount, }; enum class ReactionDisableType { None, diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index d970570a8..2f601f433 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -321,3 +321,8 @@ premiumReactionAround: 164px; premiumReactionsMiddle: 148px; premiumReactionScale: 0.70; premiumReactionInfoTop: 260px; + +premiumDot: 6px; +premiumDotPadding: margins(4px, 4px, 4px, 4px); +premiumDotsMargin: margins(0px, 5px, 0px, 6px); +premiumVideoWidth: 182px; diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 80cce885c..5556f1029 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -1353,6 +1353,12 @@ LocationType DocumentData::locationType() const { : DocumentFileLocation; } +void DocumentData::forceIsStreamedAnimation() { + type = AnimatedDocument; + _additional = nullptr; + setMaybeSupportsStreaming(true); +} + bool DocumentData::isVoiceMessage() const { return (type == VoiceDocument); } diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 41d83fae5..c660687da 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -150,6 +150,7 @@ public: [[nodiscard]] VoiceData *voice(); [[nodiscard]] const VoiceData *voice() const; + void forceIsStreamedAnimation(); [[nodiscard]] bool isVoiceMessage() const; [[nodiscard]] bool isVideoMessage() const; [[nodiscard]] bool isSong() const; diff --git a/Telegram/SourceFiles/data/data_file_origin.cpp b/Telegram/SourceFiles/data/data_file_origin.cpp index c7324ed3c..9f0426dfe 100644 --- a/Telegram/SourceFiles/data/data_file_origin.cpp +++ b/Telegram/SourceFiles/data/data_file_origin.cpp @@ -155,6 +155,11 @@ struct FileReferenceAccumulator { }, [](const MTPDaccount_savedRingtonesNotModified &data) { }); } + void push(const MTPhelp_PremiumPromo &data) { + data.match([&](const MTPDhelp_premiumPromo &data) { + push(data.vvideos()); + }); + } UpdatedFileReferences result; }; @@ -208,6 +213,10 @@ UpdatedFileReferences GetFileReferences( return GetFileReferencesHelper(data); } +UpdatedFileReferences GetFileReferences(const MTPhelp_PremiumPromo &data) { + return GetFileReferencesHelper(data); +} + UpdatedFileReferences GetFileReferences(const MTPMessageMedia &data) { return GetFileReferencesHelper(data); } diff --git a/Telegram/SourceFiles/data/data_file_origin.h b/Telegram/SourceFiles/data/data_file_origin.h index 84b382fb1..ad258ac19 100644 --- a/Telegram/SourceFiles/data/data_file_origin.h +++ b/Telegram/SourceFiles/data/data_file_origin.h @@ -102,6 +102,12 @@ struct FileOriginRingtones { } }; +struct FileOriginPremiumPreviews { + inline bool operator<(const FileOriginPremiumPreviews &) const { + return false; + } +}; + struct FileOrigin { using Variant = std::variant< v::null_t, @@ -112,7 +118,8 @@ struct FileOrigin { FileOriginSavedGifs, FileOriginWallpaper, FileOriginTheme, - FileOriginRingtones>; + FileOriginRingtones, + FileOriginPremiumPreviews>; FileOrigin() = default; FileOrigin(FileOriginMessage data) : data(data) { @@ -131,6 +138,8 @@ struct FileOrigin { } FileOrigin(FileOriginRingtones data) : data(data) { } + FileOrigin(FileOriginPremiumPreviews data) : data(data) { + } explicit operator bool() const { return !v::is_null(data); @@ -178,6 +187,7 @@ UpdatedFileReferences GetFileReferences(const MTPWallPaper &data); UpdatedFileReferences GetFileReferences(const MTPTheme &data); UpdatedFileReferences GetFileReferences( const MTPaccount_SavedRingtones &data); +UpdatedFileReferences GetFileReferences(const MTPhelp_PremiumPromo &data); // Admin Log Event. UpdatedFileReferences GetFileReferences(const MTPMessageMedia &data);