Added view button to webpages.

This commit is contained in:
23rd 2021-10-10 19:11:08 +03:00
parent 1613495425
commit 6163e922b3
10 changed files with 183 additions and 21 deletions

View File

@ -2893,6 +2893,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_view_button_bot" = "View bot";
"lng_view_button_group" = "View group";
"lng_view_button_channel" = "View channel";
"lng_view_button_background" = "View background";
"lng_view_button_theme" = "View theme";
"lng_view_button_message" = "View message";
"lng_view_button_voice_chat" = "Voice chat";
"lng_view_button_voice_chat_channel" = "Live stream";
"lng_sponsored_title" = "What are sponsored messages?";
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties cant spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";

View File

@ -146,6 +146,20 @@ WebPageType ParseWebPageType(const MTPDwebPage &page) {
return WebPageType::WallPaper;
} else if (type == qstr("telegram_theme")) {
return WebPageType::Theme;
} else if (type == qstr("telegram_channel")) {
return WebPageType::Channel;
} else if (type == qstr("telegram_message")) {
return WebPageType::Message;
} else if (type == qstr("telegram_bot")) {
return WebPageType::Bot;
} else if (type == qstr("telegram_megagroup")) {
return WebPageType::Group;
} else if (type == qstr("telegram_voicechat")) {
return WebPageType::VoiceChat;
} else if (type == qstr("telegram_livestream")) {
return WebPageType::Livestream;
} else if (type == qstr("telegram_user")) {
return WebPageType::User;
} else if (page.vcached_page()) {
return WebPageType::ArticleWithIV;
} else {
@ -302,4 +316,4 @@ void WebPageData::ApplyChanges(
});
}
session->data().sendWebPageGamePollNotifications();
}
}

View File

@ -17,13 +17,26 @@ class Session;
} // namespace Data
enum class WebPageType {
Message,
Group,
Channel,
Photo,
Video,
User,
Bot,
Profile,
WallPaper,
Theme,
Article,
ArticleWithIV,
VoiceChat,
Livestream,
};
WebPageType ParseWebPageType(const MTPDwebPage &type);

View File

@ -243,12 +243,6 @@ Message::Message(
: Element(delegate, data, replacing) {
initLogEntryOriginal();
initPsa();
if (data->isSponsored()) {
_viewButton = std::make_unique<ViewButton>(
data->displayFrom(),
[=] { history()->owner().requestViewRepaint(this); });
}
}
Message::~Message() {
@ -611,16 +605,18 @@ void Message::draw(Painter &p, const PaintContext &context) const {
auto inner = g;
paintCommentsButton(p, inner, context);
if (_viewButton) {
if (ensureViewButton()) {
_viewButton->draw(
p,
_viewButton->countSponsoredRect(inner),
_viewButton->countRect(inner),
context);
}
auto trect = inner.marginsRemoved(st::msgPadding);
if (mediaOnBottom) {
trect.setHeight(trect.height() + st::msgPadding.bottom());
trect.setHeight(trect.height()
+ st::msgPadding.bottom()
- viewButtonHeight());
}
if (mediaOnTop) {
trect.setY(trect.y() - st::msgPadding.top());
@ -1208,14 +1204,16 @@ TextState Message::textState(
if (_viewButton
&& _viewButton->getState(
point,
_viewButton->countSponsoredRect(bubble),
_viewButton->countRect(bubble),
&result)) {
return result;
}
auto trect = bubble.marginsRemoved(st::msgPadding);
if (mediaOnBottom) {
trect.setHeight(trect.height() + st::msgPadding.bottom());
trect.setHeight(trect.height()
+ st::msgPadding.bottom()
- viewButtonHeight());
}
if (mediaOnTop) {
trect.setY(trect.y() - st::msgPadding.top());
@ -1754,7 +1752,7 @@ void Message::drawInfo(
; msgsigned && !msgsigned->isAnonymousRank) {
msgsigned->signature.drawElided(p, dateX, dateY, item->_timeWidth);
} else if (const auto sponsored = displayedSponsorBadge()) {
const auto skipY = _viewButton ? _viewButton->height() : 0;
const auto skipY = viewButtonHeight();
sponsored->text.drawElided(p, dateX, dateY - skipY, item->_timeWidth);
} else if (const auto edited = displayedEditBadge()) {
edited->text.drawElided(p, dateX, dateY, item->_timeWidth);
@ -1958,6 +1956,33 @@ int Message::monospaceMaxWidth() const {
+ st::msgPadding.right();
}
int Message::viewButtonHeight() const {
return _viewButton ? _viewButton->height() : 0;
}
bool Message::ensureViewButton() const {
if (data()->isSponsored()
|| (data()->media()
&& ViewButton::MediaHasViewButton(data()->media()))) {
if (_viewButton) {
return true;
}
auto callback = [=] { history()->owner().requestViewRepaint(this); };
_viewButton = data()->isSponsored()
? std::make_unique<ViewButton>(
data()->displayFrom(),
std::move(callback))
: std::make_unique<ViewButton>(
data()->media(),
std::move(callback));
return true;
}
if (_viewButton) {
_viewButton.reset(nullptr);
}
return false;
}
void Message::initLogEntryOriginal() {
if (const auto log = message()->Get<HistoryMessageLogEntryOriginal>()) {
AddComponents(LogEntryOriginal::Bit());
@ -2618,8 +2643,8 @@ int Message::resizeContentGetHeight(int newWidth) {
if (item->repliesAreComments() || item->externalReply()) {
newHeight += st::historyCommentsButtonHeight;
}
if (_viewButton) {
newHeight += _viewButton->height();
if (ensureViewButton()) {
newHeight += viewButtonHeight();
}
} else if (mediaDisplayed) {
newHeight = media->height();

View File

@ -213,6 +213,9 @@ private:
[[nodiscard]] int plainMaxWidth() const;
[[nodiscard]] int monospaceMaxWidth() const;
[[nodiscard]] bool ensureViewButton() const;
[[nodiscard]] int viewButtonHeight() const;
WebPage *logEntryOriginal() const;
[[nodiscard]] ClickHandlerPtr createGoToCommentsLink() const;
@ -224,7 +227,7 @@ private:
mutable ClickHandlerPtr _rightActionLink;
mutable ClickHandlerPtr _fastReplyLink;
mutable std::unique_ptr<CommentsButton> _comments;
std::unique_ptr<ViewButton> _viewButton;
mutable std::unique_ptr<ViewButton> _viewButton;
Ui::Text::String _rightBadge;
int _bubbleWidthLimit = 0;

View File

@ -7,10 +7,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "history/view/history_view_view_button.h"
#include "core/application.h"
#include "core/click_handler_types.h"
#include "data/data_cloud_themes.h"
#include "data/data_session.h"
#include "data/data_sponsored_messages.h"
#include "data/data_user.h"
#include "data/data_web_page.h"
#include "history/view/history_view_cursor_state.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
@ -42,22 +45,71 @@ inline auto PeerToPhrase(not_null<PeerData*> peer) {
return Ui::Text::Upper(phrase);
}
inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
const auto type = webpage->type;
return Ui::Text::Upper((type == WebPageType::Theme)
? tr::lng_view_button_theme(tr::now)
: (type == WebPageType::Message)
? tr::lng_view_button_message(tr::now)
: (type == WebPageType::Group)
? tr::lng_view_button_group(tr::now)
: (type == WebPageType::WallPaper)
? tr::lng_view_button_background(tr::now)
: (type == WebPageType::Channel)
? tr::lng_view_button_channel(tr::now)
: (type == WebPageType::VoiceChat)
? tr::lng_view_button_voice_chat(tr::now)
: (type == WebPageType::Livestream)
? tr::lng_view_button_voice_chat_channel(tr::now)
: (type == WebPageType::Bot)
? tr::lng_view_button_bot(tr::now)
: (type == WebPageType::User)
? tr::lng_view_button_user(tr::now)
: QString());
}
} // namespace
struct ViewButton::Inner {
Inner(not_null<PeerData*> peer, Fn<void()> updateCallback);
Inner(not_null<Data::Media*> media, Fn<void()> updateCallback);
void updateMask(int height);
void toggleRipple(bool pressed);
const style::margins &margins;
const ClickHandlerPtr link;
const Fn<void()> updateCallback;
bool underDate = true;
int lastWidth = 0;
QPoint lastPoint;
std::unique_ptr<Ui::RippleAnimation> ripple;
Ui::Text::String text;
};
bool ViewButton::MediaHasViewButton(not_null<Data::Media*> media) {
return media->webpage()
? MediaHasViewButton(media->webpage())
: false;
}
bool ViewButton::MediaHasViewButton(
not_null<WebPageData*> webpage) {
const auto type = webpage->type;
return (type == WebPageType::Message)
|| (type == WebPageType::Group)
|| (type == WebPageType::Channel)
// || (type == WebPageType::Bot)
// || (type == WebPageType::User)
|| (type == WebPageType::VoiceChat)
|| (type == WebPageType::Livestream)
|| ((type == WebPageType::Theme)
&& webpage->document
&& webpage->document->isTheme())
|| ((type == WebPageType::WallPaper)
&& webpage->document
&& webpage->document->isWallPaper());
}
ViewButton::Inner::Inner(not_null<PeerData*> peer, Fn<void()> updateCallback)
: margins(st::historyViewButtonMargins)
, link(std::make_shared<LambdaClickHandler>([=](ClickContext context) {
@ -73,6 +125,26 @@ ViewButton::Inner::Inner(not_null<PeerData*> peer, Fn<void()> updateCallback)
, text(st::historyViewButtonTextStyle, PeerToPhrase(peer)) {
}
ViewButton::Inner::Inner(
not_null<Data::Media*> media,
Fn<void()> updateCallback)
: margins(st::historyViewButtonMargins)
, link(std::make_shared<LambdaClickHandler>([=](ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) {
const auto &data = controller->session().data();
const auto webpage = media->webpage();
if (!webpage) {
return;
}
HiddenUrlClickHandler::Open(webpage->url, context.other);
}
}))
, updateCallback(std::move(updateCallback))
, underDate(false)
, text(st::historyViewButtonTextStyle, WebPageToPhrase(media->webpage())) {
}
void ViewButton::Inner::updateMask(int height) {
ripple = std::make_unique<Ui::RippleAnimation>(
st::defaultRippleAnimation,
@ -96,6 +168,12 @@ ViewButton::ViewButton(not_null<PeerData*> peer, Fn<void()> updateCallback)
: _inner(std::make_unique<Inner>(peer, std::move(updateCallback))) {
}
ViewButton::ViewButton(
not_null<Data::Media*> media,
Fn<void()> updateCallback)
: _inner(std::make_unique<Inner>(media, std::move(updateCallback))) {
}
ViewButton::~ViewButton() {
}
@ -169,10 +247,11 @@ bool ViewButton::getState(
return true;
}
QRect ViewButton::countSponsoredRect(const QRect &r) const {
QRect ViewButton::countRect(const QRect &r) const {
const auto dateHeight = (_inner->underDate ? 0 : st::msgDateFont->height);
return QRect(
r.left(),
r.top() + r.height() - height(),
r.top() + r.height() - height() - dateHeight,
r.width(),
height()) - _inner->margins;
}

View File

@ -9,6 +9,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/chat_style.h"
namespace Data {
class Media;
} // namespace Data
struct WebPageData;
namespace HistoryView {
struct TextState;
@ -16,8 +22,14 @@ struct TextState;
class ViewButton {
public:
ViewButton(not_null<PeerData*> peer, Fn<void()> updateCallback);
ViewButton(not_null<Data::Media*> media, Fn<void()> updateCallback);
~ViewButton();
[[nodiscard]] static bool MediaHasViewButton(
not_null<Data::Media*> media);
[[nodiscard]] static bool MediaHasViewButton(
not_null<WebPageData*> webpage);
[[nodiscard]] int height() const;
void draw(
@ -28,7 +40,7 @@ public:
[[nodiscard]] const ClickHandlerPtr &link() const;
bool checkLink(const ClickHandlerPtr &other, bool pressed);
[[nodiscard]] QRect countSponsoredRect(const QRect &r) const;
[[nodiscard]] QRect countRect(const QRect &r) const;
[[nodiscard]] bool getState(
QPoint point,

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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/media/history_view_media_common.h"
#include "history/view/media/history_view_theme_document.h"
#include "ui/image/image.h"
@ -188,7 +189,12 @@ QSize WebPage::countOptimalSize() {
_data->url);
}
auto textFloatsAroundInfo = !_asArticle && !_attach && isBubbleBottom();
_hasViewButton = ViewButton::MediaHasViewButton(_data);
const auto textFloatsAroundInfo = !_asArticle
&& !_attach
&& isBubbleBottom()
&& !_hasViewButton;
// init strings
if (_description.isEmpty() && !_data->description.text.isEmpty()) {
@ -391,6 +397,8 @@ QSize WebPage::countCurrentSize(int newWidth) {
} else if (isBubbleBottom() && _attach->customInfoLayout() && _attach->width() + _parent->skipBlockWidth() > innerWidth + bubble.left() + bubble.right()) {
newHeight += bottomInfoPadding();
}
} else if (_hasViewButton) {
newHeight += bottomInfoPadding();
}
}
auto padding = inBubblePadding();
@ -471,6 +479,8 @@ void WebPage::draw(Painter &p, const PaintContext &context) const {
bshift += bottomInfoPadding();
} else if (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->width() + _parent->skipBlockWidth() > paintw + bubble.left() + bubble.right()) {
bshift += bottomInfoPadding();
} else if (_hasViewButton) {
bshift += bottomInfoPadding();
}
QRect bar(style::rtlrect(st::msgPadding.left(), tshift, st::webPageBar, height() - tshift - bshift, width()));

View File

@ -117,6 +117,7 @@ private:
mutable std::shared_ptr<Data::PhotoMedia> _photoMedia;
bool _asArticle = false;
bool _hasViewButton = false;
int _dataVersion = -1;
int _siteNameLines = 0;
int _titleLines = 0;

View File

@ -751,7 +751,7 @@ historyPollInChosen: icon {{ "poll_select_check", historyFileInIconFg }};
historyPollInChosenSelected: icon {{ "poll_select_check", historyFileInIconFgSelected }};
historyViewButtonHeight: 42px;
historyViewButtonMargins: margins(5px, 5px, 5px, 5px);
historyViewButtonMargins: margins(13px, 5px, 13px, 5px);
historyViewButtonOutline: margins(2px, 2px, 2px, 2px);
historyViewButtonTextStyle: semiboldTextStyle;