Show videos in premium preview boxes.

This commit is contained in:
John Preston 2022-06-10 12:03:47 +04:00
parent 1b604bed0b
commit f78b9324dc
10 changed files with 475 additions and 17 deletions

View file

@ -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<ApiWrap*> 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<TextWithEntities> Premium::statusTextValue() const {
_statusText.value_or(TextWithEntities()));
}
auto Premium::videos() const
-> const base::flat_map<QString, not_null<DocumentData*>> & {
return _videos;
}
rpl::producer<> Premium::videosUpdated() const {
return _videosUpdated.events();
}
auto Premium::stickers() const
-> const std::vector<not_null<DocumentData*>> & {
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<QString, not_null<DocumentData*>>();
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();
}

View file

@ -24,17 +24,36 @@ public:
void reload();
[[nodiscard]] rpl::producer<TextWithEntities> statusTextValue() const;
[[nodiscard]] auto videos() const
-> const base::flat_map<QString, not_null<DocumentData*>> &;
[[nodiscard]] rpl::producer<> videosUpdated() const;
[[nodiscard]] auto stickers() const
-> const std::vector<not_null<DocumentData*>> &;
[[nodiscard]] rpl::producer<> stickersUpdated() const;
[[nodiscard]] int64 monthlyAmount() const;
[[nodiscard]] QString monthlyCurrency() const;
private:
void reloadPromo();
void reloadStickers();
const not_null<Main::Session*> _session;
MTP::Sender _api;
mtpRequestId _statusRequestId = 0;
mtpRequestId _promoRequestId = 0;
std::optional<TextWithEntities> _statusText;
rpl::event_stream<TextWithEntities> _statusTextUpdates;
base::flat_map<QString, not_null<DocumentData*>> _videos;
rpl::event_stream<> _videosUpdated;
mtpRequestId _stickersRequestId = 0;
uint64 _stickersHash = 0;
std::vector<not_null<DocumentData*>> _stickers;
rpl::event_stream<> _stickersUpdated;
int64 _monthlyAmount = 0;
QString _monthlyCurrency;

View file

@ -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();
});

View file

@ -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<Data::DocumentMedia> &media) {
media->videoThumbnailWanted(origin);
}
[[nodiscard]] rpl::producer<QString> 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<QString> 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<Ui::RpWidget> ChatBackPreview(
QWidget *parent,
int height,
@ -113,10 +168,15 @@ void PreloadSticker(const std::shared_ptr<Data::DocumentMedia> &media) {
not_null<Window::SessionController*> controller,
const std::shared_ptr<Data::DocumentMedia> &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<Ui::RpWidget>(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<Data::DocumentMedia> &media) {
return result;
}
[[nodiscard]] not_null<Ui::RpWidget*> StickersPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller) {
const auto result = Ui::CreateChild<Ui::RpWidget>(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<std::shared_ptr<Data::DocumentMedia>> medias;
Ui::RpWidget *single = nullptr;
};
const auto premium = &controller->session().api().premium();
const auto state = lifetime.make_state<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<Main::Session*> 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<Ui::RpWidget*> VideoPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document) {
const auto result = Ui::CreateChild<Ui::RpWidget>(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<Media::Streaming::Document> shared,
Fn<void()> waitingCallback)
: instance(shared, std::move(waitingCallback)) {
}
QImage blurred;
Media::Streaming::Instance instance;
std::shared_ptr<Data::DocumentMedia> media;
};
const auto state = lifetime.make_state<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<Media::Streaming::Information>(update.data)
|| v::is<Media::Streaming::UpdateVideo>(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<Ui::RpWidget*> GenericPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> controller,
PremiumPreview section) {
const auto result = Ui::CreateChild<Ui::RpWidget>(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<std::shared_ptr<Data::DocumentMedia>> medias;
Ui::RpWidget *single = nullptr;
};
const auto session = &controller->session();
const auto state = lifetime.make_state<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<Ui::RpWidget>(parent.get());
result->show();
auto &lifetime = result->lifetime();
const auto state = lifetime.make_state<State>();
@ -605,6 +857,20 @@ void ReactionPreview::paintEffect(QPainter &p) {
return result;
}
[[nodiscard]] not_null<Ui::RpWidget*> GenerateDefaultPreview(
not_null<Ui::RpWidget*> parent,
not_null<Window::SessionController*> 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<Ui::AbstractButton> CreateGradientButton(
QWidget *parent,
QGradientStops stops) {
@ -641,7 +907,49 @@ void ReactionPreview::paintEffect(QPainter &p) {
return result;
}
void StickerBox(
[[nodiscard]] object_ptr<Ui::RpWidget> CreateSwitch(
not_null<Ui::RpWidget*> parent,
not_null<rpl::variable<PremiumPreview>*> 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<Ui::FixedHeightWidget>(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<Ui::AbstractButton>(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<Ui::GenericBox*> box,
not_null<Window::SessionController*> controller,
const Descriptor &descriptor,
@ -656,30 +964,46 @@ void StickerBox(
{});
struct State {
Ui::RpWidget *content = nullptr;
rpl::variable<PremiumPreview> selected;
};
const auto state = outer->lifetime().make_state<State>();
state->selected = descriptor.section;
auto text = rpl::producer<QString>();
auto title = rpl::producer<QString>();
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<Ui::FlatLabel>(
@ -700,6 +1024,9 @@ void StickerBox(
box->addRow(
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(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<Data::DocumentMedia> &media,
QImage back) {
controller->show(Box(StickerBox, controller, descriptor, media, back));
controller->show(Box(PreviewBox, controller, descriptor, media, back));
}
void Show(not_null<Window::SessionController*> controller, QImage back) {

View file

@ -22,9 +22,17 @@ void ShowStickerPreviewBox(
not_null<DocumentData*> document);
enum class PremiumPreview {
MoreUpload,
FasterDownload,
VoiceToText,
NoAds,
Reactions,
Stickers,
Avatars,
AdvancedChatManagement,
ProfileBadge,
AnimatedUserpics,
kCount,
};
enum class ReactionDisableType {
None,

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);