From 6163e922b365cca7d439ef08b35dfa9816872cb4 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 10 Oct 2021 19:11:08 +0300 Subject: [PATCH] Added view button to webpages. --- Telegram/Resources/langs/lang.strings | 5 ++ Telegram/SourceFiles/data/data_web_page.cpp | 16 +++- Telegram/SourceFiles/data/data_web_page.h | 13 +++ .../history/view/history_view_message.cpp | 53 ++++++++---- .../history/view/history_view_message.h | 5 +- .../history/view/history_view_view_button.cpp | 83 ++++++++++++++++++- .../history/view/history_view_view_button.h | 14 +++- .../view/media/history_view_web_page.cpp | 12 ++- .../view/media/history_view_web_page.h | 1 + Telegram/SourceFiles/ui/chat/chat.style | 2 +- 10 files changed, 183 insertions(+), 21 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 97731c0e40..f6501f3a46 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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 can’t 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:"; diff --git a/Telegram/SourceFiles/data/data_web_page.cpp b/Telegram/SourceFiles/data/data_web_page.cpp index d0d5b895b4..31dddecae9 100644 --- a/Telegram/SourceFiles/data/data_web_page.cpp +++ b/Telegram/SourceFiles/data/data_web_page.cpp @@ -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(); -} \ No newline at end of file +} diff --git a/Telegram/SourceFiles/data/data_web_page.h b/Telegram/SourceFiles/data/data_web_page.h index 6ec72ad484..3000361d5e 100644 --- a/Telegram/SourceFiles/data/data_web_page.h +++ b/Telegram/SourceFiles/data/data_web_page.h @@ -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); diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index b182b4f63a..6b501615de 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -243,12 +243,6 @@ Message::Message( : Element(delegate, data, replacing) { initLogEntryOriginal(); initPsa(); - - if (data->isSponsored()) { - _viewButton = std::make_unique( - 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( + data()->displayFrom(), + std::move(callback)) + : std::make_unique( + data()->media(), + std::move(callback)); + return true; + } + if (_viewButton) { + _viewButton.reset(nullptr); + } + return false; +} + void Message::initLogEntryOriginal() { if (const auto log = message()->Get()) { 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(); diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 5e5ebcd036..dfebd55432 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -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 _comments; - std::unique_ptr _viewButton; + mutable std::unique_ptr _viewButton; Ui::Text::String _rightBadge; int _bubbleWidthLimit = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_view_button.cpp b/Telegram/SourceFiles/history/view/history_view_view_button.cpp index 85f90b5d32..c9c2ce7d4f 100644 --- a/Telegram/SourceFiles/history/view/history_view_view_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_view_button.cpp @@ -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 peer) { return Ui::Text::Upper(phrase); } +inline auto WebPageToPhrase(not_null 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 peer, Fn updateCallback); + Inner(not_null media, Fn updateCallback); void updateMask(int height); void toggleRipple(bool pressed); const style::margins &margins; const ClickHandlerPtr link; const Fn updateCallback; + bool underDate = true; int lastWidth = 0; QPoint lastPoint; std::unique_ptr ripple; Ui::Text::String text; }; +bool ViewButton::MediaHasViewButton(not_null media) { + return media->webpage() + ? MediaHasViewButton(media->webpage()) + : false; +} + +bool ViewButton::MediaHasViewButton( + not_null 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 peer, Fn updateCallback) : margins(st::historyViewButtonMargins) , link(std::make_shared([=](ClickContext context) { @@ -73,6 +125,26 @@ ViewButton::Inner::Inner(not_null peer, Fn updateCallback) , text(st::historyViewButtonTextStyle, PeerToPhrase(peer)) { } +ViewButton::Inner::Inner( + not_null media, + Fn updateCallback) +: margins(st::historyViewButtonMargins) +, link(std::make_shared([=](ClickContext context) { + const auto my = context.other.value(); + 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( st::defaultRippleAnimation, @@ -96,6 +168,12 @@ ViewButton::ViewButton(not_null peer, Fn updateCallback) : _inner(std::make_unique(peer, std::move(updateCallback))) { } +ViewButton::ViewButton( + not_null media, + Fn updateCallback) +: _inner(std::make_unique(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; } diff --git a/Telegram/SourceFiles/history/view/history_view_view_button.h b/Telegram/SourceFiles/history/view/history_view_view_button.h index 8a63b6b2f6..3c9514a8c2 100644 --- a/Telegram/SourceFiles/history/view/history_view_view_button.h +++ b/Telegram/SourceFiles/history/view/history_view_view_button.h @@ -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 peer, Fn updateCallback); + ViewButton(not_null media, Fn updateCallback); ~ViewButton(); + [[nodiscard]] static bool MediaHasViewButton( + not_null media); + [[nodiscard]] static bool MediaHasViewButton( + not_null 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, diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 252a678cc6..32ed515d43 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -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())); diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.h b/Telegram/SourceFiles/history/view/media/history_view_web_page.h index aabf099440..db7bea091c 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.h +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.h @@ -117,6 +117,7 @@ private: mutable std::shared_ptr _photoMedia; bool _asArticle = false; + bool _hasViewButton = false; int _dataVersion = -1; int _siteNameLines = 0; int _titleLines = 0; diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 4331641346..4c94aebcf2 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -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;