Replaced style of sponsored messages with fake webpage.

This commit is contained in:
23rd 2023-11-15 18:18:18 +03:00 committed by John Preston
parent 17f89ba1f9
commit 4c5c2aadc4
10 changed files with 168 additions and 88 deletions

View file

@ -1823,8 +1823,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_forwarded_hidden" = "The account was hidden by the user.";
"lng_forwarded_imported" = "This message was imported from another app. It may not be real.";
"lng_signed_author" = "Author: {user}";
"lng_sponsored" = "sponsored";
"lng_recommended" = "recommended";
"lng_sponsored_message_title" = "Sponsored";
"lng_recommended_message_title" = "Recommended";
"lng_edited" = "edited";
"lng_edited_date" = "Edited: {date}";
"lng_sent_date" = "Sent: {date}";

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "data/data_channel.h"
#include "data/data_peer_id.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "history/history.h"
@ -275,39 +276,22 @@ void SponsoredMessages::append(
.isBot = (peer->isUser() && peer->asUser()->isBot()),
.isExactPost = exactPost,
.isRecommended = data.is_recommended(),
.userpic = { .location = peer->userpicLocation() },
.isForceUserpicDisplay = data.is_show_peer_photo(),
};
};
const auto externalLink = data.vwebpage()
? qs(data.vwebpage()->data().vurl())
: QString();
const auto userpicFromPhoto = [&](const MTPphoto &photo) {
return photo.match([&](const MTPDphoto &data) {
for (const auto &size : data.vsizes().v) {
const auto result = Images::FromPhotoSize(
_session,
data,
size);
if (result.location.valid()) {
return result;
}
}
return ImageWithLocation{};
}, [](const MTPDphotoEmpty &) {
return ImageWithLocation{};
});
};
const auto from = [&]() -> SponsoredFrom {
if (const auto webpage = data.vwebpage()) {
const auto &data = webpage->data();
auto userpic = data.vphoto()
? userpicFromPhoto(*data.vphoto())
: ImageWithLocation{};
const auto photoId = data.vphoto()
? _session->data().processPhoto(*data.vphoto())->id
: PhotoId(0);
return SponsoredFrom{
.title = qs(data.vsite_name()),
.externalLink = externalLink,
.userpic = std::move(userpic),
.externalLinkPhotoId = photoId,
.isForceUserpicDisplay = message.data().is_show_peer_photo(),
};
} else if (const auto fromId = data.vfrom_id()) {
@ -317,14 +301,12 @@ void SponsoredMessages::append(
}
Assert(data.vchat_invite());
return data.vchat_invite()->match([&](const MTPDchatInvite &data) {
auto userpic = userpicFromPhoto(data.vphoto());
return SponsoredFrom{
.title = qs(data.vtitle()),
.isBroadcast = data.is_broadcast(),
.isMegagroup = data.is_megagroup(),
.isChannel = data.is_channel(),
.isPublic = data.is_public(),
.userpic = std::move(userpic),
.isForceUserpicDisplay = message.data().is_show_peer_photo(),
};
}, [&](const MTPDchatInviteAlready &data) {
@ -437,7 +419,7 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails(
const auto &hash = data.chatInviteHash;
using InfoList = std::vector<TextWithEntities>;
const auto info = (!data.sponsorInfo.text.isEmpty()
auto info = (!data.sponsorInfo.text.isEmpty()
&& !data.additionalInfo.text.isEmpty())
? InfoList{ data.sponsorInfo, data.additionalInfo }
: !data.sponsorInfo.text.isEmpty()
@ -451,6 +433,12 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails(
.msgId = data.msgId,
.info = std::move(info),
.externalLink = data.externalLink,
.isForceUserpicDisplay = data.from.isForceUserpicDisplay,
.buttonText = !data.externalLink.isEmpty()
? tr::lng_view_button_external_link(tr::now)
: data.from.isBot
? tr::lng_view_button_bot(tr::now)
: QString(),
};
}

View file

@ -32,7 +32,7 @@ struct SponsoredFrom {
bool isExactPost = false;
bool isRecommended = false;
QString externalLink;
ImageWithLocation userpic;
PhotoId externalLinkPhotoId;
bool isForceUserpicDisplay = false;
};
@ -61,6 +61,8 @@ public:
MsgId msgId;
std::vector<TextWithEntities> info;
QString externalLink;
bool isForceUserpicDisplay = false;
QString buttonText;
};
using RandomId = QByteArray;
explicit SponsoredMessages(not_null<Session*> owner);

View file

@ -320,6 +320,7 @@ enum class MediaWebPageFlag : uint8 {
ForceSmallMedia = (1 << 1),
Manual = (1 << 2),
Safe = (1 << 3),
Sponsored = (1 << 4),
};
inline constexpr bool is_flag_type(MediaWebPageFlag) { return true; }
using MediaWebPageFlags = base::flags<MediaWebPageFlag>;

View file

@ -695,13 +695,42 @@ HistoryItem::HistoryItem(
? injectedAfter->date()
: 0),
/*from.peer ? from.peer->id : */PeerId(0)) {
createComponentsHelper(
_flags,
FullReplyTo(),
UserId(0), // viaBotId
QString(), // postAuthor
HistoryMessageMarkupData());
setText(textWithEntities);
_flags |= MessageFlag::Sponsored;
const auto webPageType = from.isExactPost
? WebPageType::Message
: from.isBot
? WebPageType::Bot
: from.isBroadcast
? WebPageType::Channel
: (from.peer && from.peer->isUser())
? WebPageType::User
: WebPageType::Group;
const auto webpage = history->peer->owner().webpage(
history->peer->owner().nextLocalMessageId().bare,
webPageType,
from.externalLink,
from.externalLink,
from.isRecommended
? tr::lng_recommended_message_title(tr::now)
: tr::lng_sponsored_message_title(tr::now),
from.title,
textWithEntities,
from.externalLinkPhotoId
? history->owner().photo(from.externalLinkPhotoId)
: nullptr,
nullptr,
WebPageCollage(),
0,
QString(),
false,
0);
auto webpageMedia = std::make_unique<Data::MediaWebPage>(
this,
webpage,
MediaWebPageFlag::Sponsored);
_media = std::move(webpageMedia);
setSponsoredFrom(from);
}

View file

@ -445,11 +445,14 @@ QSize BottomInfo::countCurrentSize(int newWidth) {
if (newWidth >= maxWidth()) {
return optimalSize();
}
const auto dateHeight = (_data.flags & Data::Flag::Sponsored)
? 0
: st::msgDateFont->height;
const auto noReactionsWidth = maxWidth() - _reactionsMaxWidth;
accumulate_min(newWidth, std::max(noReactionsWidth, _reactionsMaxWidth));
return QSize(
newWidth,
st::msgDateFont->height + countReactionsHeight(newWidth));
dateHeight + countReactionsHeight(newWidth));
}
void BottomInfo::layout() {
@ -478,10 +481,8 @@ void BottomInfo::layoutDateText() {
const auto name = _authorElided
? st::msgDateFont->elided(author, maxWidth - afterAuthorWidth)
: author;
const auto full = (_data.flags & Data::Flag::Recommended)
? tr::lng_recommended(tr::now)
: (_data.flags & Data::Flag::Sponsored)
? tr::lng_sponsored(tr::now)
const auto full = (_data.flags & Data::Flag::Sponsored)
? QString()
: (_data.flags & Data::Flag::Imported)
? (date + ' ' + tr::lng_imported(tr::now))
: name.isEmpty()
@ -568,7 +569,10 @@ QSize BottomInfo::countOptimalSize() {
}
_reactionsMaxWidth = countReactionsMaxWidth();
width += _reactionsMaxWidth;
return QSize(width, st::msgDateFont->height);
const auto dateHeight = (_data.flags & Data::Flag::Sponsored)
? 0
: st::msgDateFont->height;
return QSize(width, dateHeight);
}
BottomInfo::Reaction BottomInfo::prepareReactionWithId(
@ -644,10 +648,7 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
if (message->context() == Context::Replies) {
result.flags |= Flag::RepliesContext;
}
if (const auto sponsored = item->Get<HistoryMessageSponsored>()) {
if (sponsored->recommended) {
result.flags |= Flag::Recommended;
}
if (item->isSponsored()) {
result.flags |= Flag::Sponsored;
}
if (item->isPinned() && message->context() != Context::Pinned) {

View file

@ -44,7 +44,6 @@ public:
Sponsored = 0x10,
Pinned = 0x20,
Imported = 0x40,
Recommended = 0x80,
//Unread, // We don't want to pass and update it in Date for now.
};
friend inline constexpr bool is_flag_type(Flag) { return true; };

View file

@ -1996,14 +1996,6 @@ bool Message::hasFromPhoto() const {
case Context::Replies: {
const auto item = data();
if (item->isPost()) {
if (item->isSponsored()) {
if (item->history()->peer->isMegagroup()) {
return true;
}
if (const auto info = item->Get<HistoryMessageSponsored>()) {
return info->isForceUserpicDisplay;
}
}
return false;
}
if (item->isEmpty()
@ -3098,10 +3090,8 @@ int Message::viewButtonHeight() const {
void Message::updateViewButtonExistence() {
const auto item = data();
const auto sponsored = item->Get<HistoryMessageSponsored>();
const auto media = sponsored ? nullptr : item->media();
const auto has = sponsored
|| (media && ViewButton::MediaHasViewButton(media));
const auto media = item->media();
const auto has = (media && ViewButton::MediaHasViewButton(media));
if (!has) {
_viewButton = nullptr;
return;
@ -3114,7 +3104,7 @@ void Message::updateViewButtonExistence() {
colorIndex(),
[=] { repaint(); });
};
_viewButton = sponsored ? make(sponsored) : make(media);
_viewButton = make(media);
}
void Message::initLogEntryOriginal() {
@ -3200,7 +3190,7 @@ bool Message::hasFromName() const {
}
bool Message::displayFromName() const {
if (!hasFromName() || isAttachedToPrevious()) {
if (!hasFromName() || isAttachedToPrevious() || data()->isSponsored()) {
return false;
}
return !Has<PsaTooltipState>();

View file

@ -13,9 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item_components.h"
#include "history/history_item.h"
#include "history/history.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/history_view_view_button.h"
#include "history/view/history_view_element.h"
#include "history/view/history_view_sponsored_click_handler.h"
#include "history/view/media/history_view_media_common.h"
#include "history/view/media/history_view_theme_document.h"
#include "ui/image/image.h"
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h"
#include "ui/painter.h"
#include "ui/power_saving.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_wall_paper.h"
#include "data/data_media_types.h"
@ -35,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo_media.h"
#include "data/data_file_click_handler.h"
#include "data/data_file_origin.h"
#include "data/data_sponsored_messages.h"
#include "styles/style_chat.h"
namespace HistoryView {
@ -117,23 +119,7 @@ std::vector<std::unique_ptr<Data::Media>> PrepareCollageMedia(
: QString());
}
} // namespace
WebPage::WebPage(
not_null<Element*> parent,
not_null<WebPageData*> data,
MediaWebPageFlags flags)
: Media(parent)
, _st(st::historyPagePreview)
, _data(data)
, _siteName(st::msgMinWidth - _st.padding.left() - _st.padding.right())
, _title(st::msgMinWidth - _st.padding.left() - _st.padding.right())
, _description(st::msgMinWidth - _st.padding.left() - _st.padding.right())
, _flags(flags) {
history()->owner().registerWebPageView(_data, _parent);
}
bool WebPage::HasButton(not_null<WebPageData*> webpage) {
[[nodiscard]] bool HasButton(not_null<WebPageData*> webpage) {
const auto type = webpage->type;
return (type == WebPageType::Message)
|| (type == WebPageType::Group)
@ -154,6 +140,43 @@ bool WebPage::HasButton(not_null<WebPageData*> webpage) {
&& webpage->document->isWallPaper());
}
} // namespace
WebPage::WebPage(
not_null<Element*> parent,
not_null<WebPageData*> data,
MediaWebPageFlags flags)
: Media(parent)
, _st(st::historyPagePreview)
, _data(data)
, _sponsoredData([&]() -> std::optional<SponsoredData> {
if (!(flags & MediaWebPageFlag::Sponsored)) {
return std::nullopt;
}
const auto &data = _parent->data()->history()->owner();
const auto details = data.sponsoredMessages().lookupDetails(
_parent->data()->fullId());
auto result = std::make_optional<SponsoredData>();
result->buttonText = details.buttonText;
result->hasExternalLink = (details.externalLink == _data->url);
#ifdef _DEBUG
if (details.peer) {
#else
if (details.isForceUserpicDisplay && details.peer) {
#endif
result->peer = details.peer;
result->userpicView = details.peer->createUserpicView();
details.peer->loadUserpic();
}
return result;
}())
, _siteName(st::msgMinWidth - _st.padding.left() - _st.padding.right())
, _title(st::msgMinWidth - _st.padding.left() - _st.padding.right())
, _description(st::msgMinWidth - _st.padding.left() - _st.padding.right())
, _flags(flags) {
history()->owner().registerWebPageView(_data, _parent);
}
QSize WebPage::countOptimalSize() {
if (_data->pendingTill || _data->failed) {
return { 0, 0 };
@ -165,6 +188,11 @@ QSize WebPage::countOptimalSize() {
if (HasButton(_data)) {
_openButton = PageToPhrase(_data);
_openButtonWidth = st::semiboldFont->width(_openButton);
} else if (_sponsoredData) {
if (!_sponsoredData->buttonText.isEmpty()) {
_openButton = Ui::Text::Upper(_sponsoredData->buttonText);
_openButtonWidth = st::semiboldFont->width(_openButton);
}
}
const auto padding = inBubblePadding() + innerMargin();
@ -183,7 +211,7 @@ QSize WebPage::countOptimalSize() {
}
auto lineHeight = UnitedLineHeight();
if (!_openl && !_data->url.isEmpty()) {
if (!_openl && (!_data->url.isEmpty() || _sponsoredData)) {
const auto previewOfHiddenUrl = [&] {
if (_data->type == WebPageType::BotApp) {
// Bot Web Apps always show confirmation on hidden urls.
@ -233,6 +261,11 @@ QSize WebPage::countOptimalSize() {
_data->document,
_parent->data()->fullId());
}
if (_sponsoredData) {
_openl = SponsoredLink(_sponsoredData->hasExternalLink
? _data->url
: QString());
}
}
// init layout
@ -375,13 +408,20 @@ QSize WebPage::countCurrentSize(int newWidth) {
auto newHeight = 0;
auto lineHeight = UnitedLineHeight();
auto linesMax = isLogEntryOriginal() ? kMaxOriginalEntryLines : 5;
auto linesMax = (_sponsoredData || isLogEntryOriginal())
? kMaxOriginalEntryLines
: 5;
auto siteNameHeight = _siteNameLines ? lineHeight : 0;
if (asArticle()) {
_pixh = linesMax * lineHeight;
const auto asSponsored = (!!_sponsoredData);
if (asArticle() || asSponsored) {
const auto article = asArticle();
constexpr auto kSponsoredUserpicLines = 2;
_pixh = (asSponsored ? kSponsoredUserpicLines : linesMax) * lineHeight;
do {
_pixw = articleThumbWidth(_data->photo, _pixh);
auto wleft = innerWidth - st::webPagePhotoDelta - qMax(_pixw, lineHeight);
_pixw = asSponsored ? _pixh : articleThumbWidth(_data->photo, _pixh);
auto wleft = asSponsored
? innerWidth - st::webPagePhotoDelta - qMax(_pixw, lineHeight)
: innerWidth;
newHeight = siteNameHeight;
@ -494,7 +534,9 @@ void WebPage::ensurePhotoMediaCreated() const {
}
bool WebPage::hasHeavyPart() const {
return _photoMedia || (_attach ? _attach->hasHeavyPart() : false);
return _photoMedia
|| (_sponsoredData && !_sponsoredData->userpicView.null())
|| (_attach ? _attach->hasHeavyPart() : false);
}
void WebPage::unloadHeavyPart() {
@ -503,6 +545,9 @@ void WebPage::unloadHeavyPart() {
}
_description.unloadPersistentAnimation();
_photoMedia = nullptr;
if (_sponsoredData) {
_sponsoredData->userpicView = Ui::PeerUserpicView();
}
}
void WebPage::draw(Painter &p, const PaintContext &context) const {
@ -576,6 +621,21 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
st->msgSelectOverlayCorners(Ui::CachedCornerRadius::Small));
}
paintw -= pw + st::webPagePhotoDelta;
} else if (_sponsoredData && _sponsoredData->peer) {
const auto size = _pixh;
const auto sizeHq = size * style::DevicePixelRatio();
const auto userpicPos = QPoint(inner.left() + paintw - size, tshift);
const auto &peer = _sponsoredData->peer;
auto &view = _sponsoredData->userpicView;
if (const auto cloud = peer->userpicCloudImage(view)) {
Ui::ValidateUserpicCache(view, cloud, nullptr, sizeHq, true);
p.drawImage(QRect(userpicPos, QSize(size, size)), view.cached);
} else {
const auto r = sizeHq * Ui::ForumUserpicRadiusMultiplier();
const auto empty = peer->generateUserpicImage(view, sizeHq, r);
p.drawImage(QRect(userpicPos, QSize(size, size)), empty);
}
// paintw -= size + st::webPagePhotoDelta;
}
if (_siteNameLines) {
p.setPen(cache->icon);
@ -707,6 +767,9 @@ TextState WebPage::textState(QPoint point, StateRequest request) const {
const auto bubble = _attach ? _attach->bubbleMargins() : QMargins();
const auto full = QRect(0, 0, width(), height());
auto outer = full.marginsRemoved(inBubblePadding());
if (_sponsoredData) {
outer.translate(0, st::msgDateFont->height);
}
auto inner = outer.marginsRemoved(innerMargin());
auto tshift = inner.top();
auto paintw = inner.width();
@ -790,7 +853,7 @@ TextState WebPage::textState(QPoint point, StateRequest request) const {
}
}
}
if (!result.link && outer.contains(point)) {
if ((!result.link || _sponsoredData) && outer.contains(point)) {
result.link = _openl;
}
_lastPoint = point - outer.topLeft();

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "history/view/media/history_view_media.h"
#include "ui/userpic_view.h"
namespace Data {
class Media;
@ -27,8 +28,6 @@ public:
not_null<WebPageData*> data,
MediaWebPageFlags flags);
[[nodiscard]] static bool HasButton(not_null<WebPageData*> data);
void refreshParentId(not_null<HistoryItem*> realParent) override;
void draw(Painter &p, const PaintContext &context) const override;
@ -131,6 +130,14 @@ private:
mutable std::shared_ptr<Data::PhotoMedia> _photoMedia;
mutable std::unique_ptr<Ui::RippleAnimation> _ripple;
struct SponsoredData final {
PeerData *peer = nullptr;
Ui::PeerUserpicView userpicView;
QString buttonText;
bool hasExternalLink = false;
};
mutable std::optional<SponsoredData> _sponsoredData;
int _dataVersion = -1;
int _siteNameLines = 0;
int _descriptionLines = 0;