Support sponsored peers in search results.

This commit is contained in:
John Preston 2025-03-20 17:06:02 +04:00
parent 33c5b35444
commit a0764190f2
17 changed files with 546 additions and 227 deletions

View file

@ -4392,6 +4392,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_search_filter_private" = "Private chats"; "lng_search_filter_private" = "Private chats";
"lng_search_filter_group" = "Group chats"; "lng_search_filter_group" = "Group chats";
"lng_search_filter_channel" = "Channels"; "lng_search_filter_channel" = "Channels";
"lng_search_sponsored_button" = "Ad ⋮";
"lng_media_save_progress" = "{ready} of {total} {mb}"; "lng_media_save_progress" = "{ready} of {total} {mb}";
"lng_mediaview_save_as" = "Save As..."; "lng_mediaview_save_as" = "Save As...";
@ -5806,6 +5807,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sponsored_revenued_info1_title" = "Respect Your Privacy"; "lng_sponsored_revenued_info1_title" = "Respect Your Privacy";
"lng_sponsored_revenued_info1_description" = "Ads on Telegram do not use your personal information and are based on the channel in which you see them."; "lng_sponsored_revenued_info1_description" = "Ads on Telegram do not use your personal information and are based on the channel in which you see them.";
"lng_sponsored_revenued_info1_bot_description" = "Ads on Telegram do not use your personal information and are based on the mini app in which you see them."; "lng_sponsored_revenued_info1_bot_description" = "Ads on Telegram do not use your personal information and are based on the mini app in which you see them.";
"lng_sponsored_revenued_info1_search_description" = "Ads on Telegram do not use your personal information and are based on the search query you entered.";
"lng_sponsored_revenued_info2_title" = "Help the Channel Creator"; "lng_sponsored_revenued_info2_title" = "Help the Channel Creator";
"lng_sponsored_revenued_info2_bot_title" = "Help the Bot Developer"; "lng_sponsored_revenued_info2_bot_title" = "Help the Bot Developer";
"lng_sponsored_revenued_info2_description" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed."; "lng_sponsored_revenued_info2_description" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed.";
@ -5814,9 +5816,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_sponsored_revenued_info3_description#one" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; "lng_sponsored_revenued_info3_description#one" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers.";
"lng_sponsored_revenued_info3_description#other" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; "lng_sponsored_revenued_info3_description#other" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers.";
"lng_sponsored_revenued_info3_bot_description" = "You can turn off ads in mini apps by subscribing to {link}."; "lng_sponsored_revenued_info3_bot_description" = "You can turn off ads in mini apps by subscribing to {link}.";
"lng_sponsored_revenued_info3_search_description" = "You can turn off ads by subscribing to Telegram Premium. {link}";
"lng_sponsored_revenued_info3_search_link" = "Subscribe {arrow}";
"lng_sponsored_revenued_footer_title" = "Can I Launch an Ad?"; "lng_sponsored_revenued_footer_title" = "Can I Launch an Ad?";
"lng_sponsored_revenued_footer_description" = "Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}"; "lng_sponsored_revenued_footer_description" = "Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}";
"lng_sponsored_revenued_footer_bot_description" = "Anyone can create an ad to display in this bot — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}"; "lng_sponsored_revenued_footer_bot_description" = "Anyone can create an ad to display in this bot — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}";
"lng_sponsored_revenued_footer_search_description" = "Anyone can create an ad to display in search results for any query. Check out the **Telegram Ad Platform** for details. {link}";
"lng_sponsored_top_bar_hide" = "remove"; "lng_sponsored_top_bar_hide" = "remove";
"lng_telegram_features_url" = "https://t.me/TelegramTips"; "lng_telegram_features_url" = "https://t.me/TelegramTips";

View file

@ -106,9 +106,10 @@ void PeerSearch::requestSponsored() {
parsed.sponsored.push_back({ parsed.sponsored.push_back({
.peer = _session->data().peer(peerId), .peer = _session->data().peer(peerId),
.randomId = data.vrandom_id().v, .randomId = data.vrandom_id().v,
.sponsorInfo = qs(data.vsponsor_info().value_or_empty()), .sponsorInfo = TextWithEntities::Simple(
.additionalInfo = qs( qs(data.vsponsor_info().value_or_empty())),
data.vadditional_info().value_or_empty()), .additionalInfo = TextWithEntities::Simple(
qs(data.vadditional_info().value_or_empty())),
}); });
} }
finishSponsored(requestId, std::move(parsed)); finishSponsored(requestId, std::move(parsed));

View file

@ -16,8 +16,8 @@ namespace Api {
struct SponsoredSearchResult { struct SponsoredSearchResult {
not_null<PeerData*> peer; not_null<PeerData*> peer;
QByteArray randomId; QByteArray randomId;
QString sponsorInfo; TextWithEntities sponsorInfo;
QString additionalInfo; TextWithEntities additionalInfo;
}; };
struct PeerSearchResult { struct PeerSearchResult {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/components/sponsored_messages.h" #include "data/components/sponsored_messages.h"
#include "api/api_text_entities.h" #include "api/api_text_entities.h"
#include "api/api_peer_search.h" // SponsoredSearchResult
#include "apiwrap.h" #include "apiwrap.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -33,6 +34,19 @@ constexpr auto kRequestTimeLimit = 5 * 60 * crl::time(1000);
return (received > 0) && (received + kRequestTimeLimit > crl::now()); return (received > 0) && (received + kRequestTimeLimit > crl::now());
} }
template <typename Fields>
[[nodiscard]] std::vector<TextWithEntities> Prepare(const Fields &fields) {
using InfoList = std::vector<TextWithEntities>;
return (!fields.sponsorInfo.text.isEmpty()
&& !fields.additionalInfo.text.isEmpty())
? InfoList{ fields.sponsorInfo, fields.additionalInfo }
: !fields.sponsorInfo.text.isEmpty()
? InfoList{ fields.sponsorInfo }
: !fields.additionalInfo.text.isEmpty()
? InfoList{ fields.additionalInfo }
: InfoList{};
}
} // namespace } // namespace
SponsoredMessages::SponsoredMessages(not_null<Main::Session*> session) SponsoredMessages::SponsoredMessages(not_null<Main::Session*> session)
@ -535,18 +549,8 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails(
return {}; return {};
} }
const auto &data = entryPtr->sponsored; const auto &data = entryPtr->sponsored;
using InfoList = std::vector<TextWithEntities>;
auto info = (!data.sponsorInfo.text.isEmpty()
&& !data.additionalInfo.text.isEmpty())
? InfoList{ data.sponsorInfo, data.additionalInfo }
: !data.sponsorInfo.text.isEmpty()
? InfoList{ data.sponsorInfo }
: !data.additionalInfo.text.isEmpty()
? InfoList{ data.additionalInfo }
: InfoList{};
return { return {
.info = std::move(info), .info = Prepare(data),
.link = data.link, .link = data.link,
.buttonText = data.from.buttonText, .buttonText = data.from.buttonText,
.photoId = data.from.photoId, .photoId = data.from.photoId,
@ -559,6 +563,14 @@ SponsoredMessages::Details SponsoredMessages::lookupDetails(
}; };
} }
SponsoredMessages::Details SponsoredMessages::lookupDetails(
const Api::SponsoredSearchResult &data) const {
return {
.info = Prepare(data),
.canReport = true,
};
}
void SponsoredMessages::clicked( void SponsoredMessages::clicked(
const FullMsgId &fullId, const FullMsgId &fullId,
bool isMedia, bool isMedia,
@ -583,9 +595,29 @@ void SponsoredMessages::clicked(
)).send(); )).send();
} }
SponsoredReportAction SponsoredMessages::createReportCallback(
const FullMsgId &fullId) {
const auto entry = find(fullId);
if (!entry) {
return { .callback = [=](const auto &...) {} };
}
const auto history = _session->data().history(fullId.peer);
const auto erase = [=] {
const auto it = _data.find(history);
if (it != end(_data)) {
auto &list = it->second.entries;
const auto proj = [&](const Entry &e) {
return e.itemFullId == fullId;
};
list.erase(ranges::remove_if(list, proj), end(list));
}
};
return createReportCallback(entry->sponsored.randomId, erase);
}
auto SponsoredMessages::createReportCallback(const FullMsgId &fullId) SponsoredReportAction SponsoredMessages::createReportCallback(
-> Fn<void(SponsoredReportResult::Id, Fn<void(SponsoredReportResult)>)> { const QByteArray &randomId,
Fn<void()> erase) {
using TLChoose = MTPDchannels_sponsoredMessageReportResultChooseOption; using TLChoose = MTPDchannels_sponsoredMessageReportResultChooseOption;
using TLAdsHidden = MTPDchannels_sponsoredMessageReportResultAdsHidden; using TLAdsHidden = MTPDchannels_sponsoredMessageReportResultAdsHidden;
using TLReported = MTPDchannels_sponsoredMessageReportResultReported; using TLReported = MTPDchannels_sponsoredMessageReportResultReported;
@ -601,25 +633,7 @@ auto SponsoredMessages::createReportCallback(const FullMsgId &fullId)
}; };
const auto state = std::make_shared<State>(); const auto state = std::make_shared<State>();
return [=](Result::Id optionId, Fn<void(Result)> done) { return { .callback = [=](Result::Id optionId, Fn<void(Result)> done) {
const auto entry = find(fullId);
if (!entry) {
return;
}
const auto history = _session->data().history(fullId.peer);
const auto erase = [=] {
const auto it = _data.find(history);
if (it != end(_data)) {
auto &list = it->second.entries;
const auto proj = [&](const Entry &e) {
return e.itemFullId == fullId;
};
list.erase(ranges::remove_if(list, proj), end(list));
}
};
if (optionId == Result::Id("-1")) { if (optionId == Result::Id("-1")) {
erase(); erase();
return; return;
@ -627,7 +641,7 @@ auto SponsoredMessages::createReportCallback(const FullMsgId &fullId)
state->requestId = _session->api().request( state->requestId = _session->api().request(
MTPmessages_ReportSponsoredMessage( MTPmessages_ReportSponsoredMessage(
MTP_bytes(entry->sponsored.randomId), MTP_bytes(randomId),
MTP_bytes(optionId)) MTP_bytes(optionId))
).done([=]( ).done([=](
const MTPchannels_SponsoredMessageReportResult &result, const MTPchannels_SponsoredMessageReportResult &result,
@ -664,7 +678,7 @@ auto SponsoredMessages::createReportCallback(const FullMsgId &fullId)
done({ .error = error.type() }); done({ .error = error.type() });
} }
}).send(); }).send();
}; } };
} }
SponsoredMessages::State SponsoredMessages::state( SponsoredMessages::State SponsoredMessages::state(

View file

@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History; class History;
namespace Api {
struct SponsoredSearchResult;
} // namespace Api
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -69,6 +73,25 @@ struct SponsoredMessage {
TextWithEntities additionalInfo; TextWithEntities additionalInfo;
}; };
struct SponsoredMessageDetails {
std::vector<TextWithEntities> info;
QString link;
QString buttonText;
PhotoId photoId = PhotoId(0);
PhotoId mediaPhotoId = PhotoId(0);
DocumentId mediaDocumentId = DocumentId(0);
uint64 backgroundEmojiId = 0;
uint8 colorIndex : 6 = 0;
bool isLinkInternal = false;
bool canReport = false;
};
struct SponsoredReportAction {
Fn<void(
Data::SponsoredReportResult::Id,
Fn<void(Data::SponsoredReportResult)>)> callback;
};
class SponsoredMessages final { class SponsoredMessages final {
public: public:
enum class AppendResult { enum class AppendResult {
@ -82,18 +105,7 @@ public:
InjectToMiddle, InjectToMiddle,
AppendToTopBar, AppendToTopBar,
}; };
struct Details { using Details = SponsoredMessageDetails;
std::vector<TextWithEntities> info;
QString link;
QString buttonText;
PhotoId photoId = PhotoId(0);
PhotoId mediaPhotoId = PhotoId(0);
DocumentId mediaDocumentId = DocumentId(0);
uint64 backgroundEmojiId = 0;
uint8 colorIndex : 6 = 0;
bool isLinkInternal = false;
bool canReport = false;
};
using RandomId = QByteArray; using RandomId = QByteArray;
explicit SponsoredMessages(not_null<Main::Session*> session); explicit SponsoredMessages(not_null<Main::Session*> session);
~SponsoredMessages(); ~SponsoredMessages();
@ -103,6 +115,8 @@ public:
void request(not_null<History*> history, Fn<void()> done); void request(not_null<History*> history, Fn<void()> done);
void clearItems(not_null<History*> history); void clearItems(not_null<History*> history);
[[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const; [[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const;
[[nodiscard]] Details lookupDetails(
const Api::SponsoredSearchResult &data) const;
void clicked(const FullMsgId &fullId, bool isMedia, bool isFullscreen); void clicked(const FullMsgId &fullId, bool isMedia, bool isFullscreen);
void clicked( void clicked(
const QByteArray &randomId, const QByteArray &randomId,
@ -125,8 +139,11 @@ public:
[[nodiscard]] State state(not_null<History*> history) const; [[nodiscard]] State state(not_null<History*> history) const;
[[nodiscard]] auto createReportCallback(const FullMsgId &fullId) [[nodiscard]] SponsoredReportAction createReportCallback(
-> Fn<void(SponsoredReportResult::Id, Fn<void(SponsoredReportResult)>)>; const FullMsgId &fullId);
[[nodiscard]] SponsoredReportAction createReportCallback(
const QByteArray &randomId,
Fn<void()> erase);
void clear(); void clear();

View file

@ -24,6 +24,10 @@ DialogRow {
unreadMarkDiameter: pixels; unreadMarkDiameter: pixels;
tagTop: pixels; tagTop: pixels;
} }
DialogRightButton {
button: RoundButton;
margin: margins;
}
ThreeStateIcon { ThreeStateIcon {
icon: icon; icon: icon;
@ -115,11 +119,16 @@ dialogRowFilterTagSkip: 4px;
dialogRowFilterTagStyle: TextStyle(defaultTextStyle) { dialogRowFilterTagStyle: TextStyle(defaultTextStyle) {
font: font(10px); font: font(10px);
} }
dialogRowOpenBotTextStyle: semiboldTextStyle; dialogRowOpenBot: DialogRightButton {
dialogRowOpenBotHeight: 20px; button: RoundButton(defaultActiveButton) {
dialogRowOpenBotRight: 10px; height: 20px;
dialogRowOpenBotTop: 32px; textTop: 1px;
dialogRowOpenBotRecentTop: 28px; }
margin: margins(0px, 32px, 10px, 0px);
}
dialogRowOpenBotRecent: DialogRightButton(dialogRowOpenBot) {
margin: margins(0px, 32px, 28px, 0px);
}
forumDialogJumpArrow: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFg }}; forumDialogJumpArrow: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFg }};
forumDialogJumpArrowOver: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFgOver }}; forumDialogJumpArrowOver: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFgOver }};
@ -792,3 +801,15 @@ dialogsPopularAppsAbout: FlatLabel(boxDividerLabel) {
dialogsQuickActionSize: 20px; dialogsQuickActionSize: 20px;
dialogsQuickActionRippleSize: 80px; dialogsQuickActionRippleSize: 80px;
dialogsSponsoredButton: DialogRightButton(dialogRowOpenBot) {
button: RoundButton(defaultLightButton) {
textFg: windowActiveTextFg;
textFgOver: windowActiveTextFg;
textBg: lightButtonBgOver;
textBgOver: lightButtonBgOver;
height: 20px;
textTop: 1px;
}
margin: margins(0px, 9px, 10px, 0px);
}

View file

@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
namespace style {
struct DialogRightButton;
} // namespace style
namespace Ui { namespace Ui {
class RippleAnimation; class RippleAnimation;
} // namespace Ui } // namespace Ui
@ -114,11 +118,16 @@ struct RowsByLetter {
}; };
struct RightButton final { struct RightButton final {
const style::DialogRightButton *st = nullptr;
QImage bg; QImage bg;
QImage selectedBg; QImage selectedBg;
QImage activeBg; QImage activeBg;
Ui::Text::String text; Ui::Text::String text;
std::unique_ptr<Ui::RippleAnimation> ripple; std::unique_ptr<Ui::RippleAnimation> ripple;
explicit operator bool() const {
return st != nullptr;
}
}; };
} // namespace Dialogs } // namespace Dialogs

View file

@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_session_settings.h" #include "main/main_session_settings.h"
#include "menu/menu_sponsored.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
@ -241,12 +242,17 @@ struct InnerWidget::HashtagResult {
BasicRow row; BasicRow row;
}; };
struct InnerWidget::SponsoredSearchResult {
Api::SponsoredSearchResult data;
RightButton button;
};
struct InnerWidget::PeerSearchResult { struct InnerWidget::PeerSearchResult {
explicit PeerSearchResult(not_null<PeerData*> peer) : peer(peer) { explicit PeerSearchResult(not_null<PeerData*> peer) : peer(peer) {
} }
not_null<PeerData*> peer; not_null<PeerData*> peer;
std::unique_ptr<Api::SponsoredSearchResult> sponsored; std::unique_ptr<SponsoredSearchResult> sponsored;
mutable Ui::Text::String name; mutable Ui::Text::String name;
mutable Ui::PeerBadge badge; mutable Ui::PeerBadge badge;
BasicRow row; BasicRow row;
@ -287,6 +293,12 @@ InnerWidget::InnerWidget(
_topicJumpCache = nullptr; _topicJumpCache = nullptr;
_chatsFilterTags.clear(); _chatsFilterTags.clear();
_rightButtons.clear(); _rightButtons.clear();
_pressedRightButtonData = nullptr;
for (const auto &result : _peerSearchResults) {
if (const auto sponsored = result->sponsored.get()) {
sponsored->button = {};
}
}
}, lifetime()); }, lifetime());
session().downloaderTaskFinished( session().downloaderTaskFinished(
@ -1139,17 +1151,32 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
&& r.y() <= (skip + from * st::dialogsRowHeight) && r.y() <= (skip + from * st::dialogsRowHeight)
&& r.y() + r.height() >= (skip + (from + 1) * st::dialogsRowHeight)) { && r.y() + r.height() >= (skip + (from + 1) * st::dialogsRowHeight)) {
session().sponsoredMessages().view( session().sponsoredMessages().view(
result->sponsored->randomId); result->sponsored->data.randomId);
} }
const auto peer = result->peer; const auto peer = result->peer;
const auto active = !activeEntry.fullId const auto active = !activeEntry.fullId
&& activePeer && activePeer
&& ((peer == activePeer) && ((peer == activePeer)
|| (peer->migrateTo() == activePeer)); || (peer->migrateTo() == activePeer));
const auto selected = (from == (isPressed() const auto selected = (from == ((_peerSearchMenu >= 0)
? _peerSearchMenu
: isPressed()
? _peerSearchPressed ? _peerSearchPressed
: _peerSearchSelected)); : _peerSearchSelected));
if (result->sponsored
&& result->sponsored->button.text.isEmpty()) {
fillRightButton(
result->sponsored->button,
tr::lng_search_sponsored_button(
tr::now,
Ui::Text::WithEntities),
st::dialogsSponsoredButton);
}
paintPeerSearchResult(p, result.get(), { paintPeerSearchResult(p, result.get(), {
.rightButton = (result->sponsored
? &result->sponsored->button
: nullptr),
.st = &st::defaultDialogRow, .st = &st::defaultDialogRow,
.currentBg = currentBg(), .currentBg = currentBg(),
.now = ms, .now = ms,
@ -1307,36 +1334,47 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
} }
} }
void InnerWidget::fillRightButton(
RightButton &button,
const TextWithEntities &text,
const style::DialogRightButton &st) {
button.st = &st;
button.text.setMarkedText(st.button.style, text);
const auto size = QSize(
button.text.maxWidth() + button.text.minHeight(),
st.button.height);
const auto generateBg = [&](const style::color &c) {
auto bg = QImage(
style::DevicePixelRatio() * size,
QImage::Format_ARGB32_Premultiplied);
bg.setDevicePixelRatio(style::DevicePixelRatio());
bg.fill(Qt::transparent);
{
auto p = QPainter(&bg);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(c);
const auto r = size.height() / 2;
p.drawRoundedRect(Rect(size), r, r);
}
return bg;
};
button.bg = generateBg(st.button.textBg);
button.selectedBg = generateBg(st.button.textBgOver);
button.activeBg = generateBg(st.button.textFg);
}
[[nodiscard]] RightButton *InnerWidget::maybeCacheRightButton(Row *row) { [[nodiscard]] RightButton *InnerWidget::maybeCacheRightButton(Row *row) {
if (const auto user = MaybeBotWithApp(row)) { if (const auto user = MaybeBotWithApp(row)) {
const auto it = _rightButtons.find(user->id); const auto it = _rightButtons.find(user->id);
if (it == _rightButtons.end()) { if (it == _rightButtons.end()) {
auto rightButton = RightButton(); auto rightButton = RightButton();
const auto text = tr::lng_profile_open_app_short(tr::now); fillRightButton(
rightButton.text.setText(st::dialogRowOpenBotTextStyle, text); rightButton,
const auto size = QSize( tr::lng_profile_open_app_short(
rightButton.text.maxWidth() tr::now,
+ rightButton.text.minHeight(), Ui::Text::WithEntities),
st::dialogRowOpenBotHeight); st::dialogRowOpenBot);
const auto generateBg = [&](const style::color &c) {
auto bg = QImage(
style::DevicePixelRatio() * size,
QImage::Format_ARGB32_Premultiplied);
bg.setDevicePixelRatio(style::DevicePixelRatio());
bg.fill(Qt::transparent);
{
auto p = QPainter(&bg);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(c);
const auto r = size.height() / 2;
p.drawRoundedRect(Rect(size), r, r);
}
return bg;
};
rightButton.bg = generateBg(st::activeButtonBg);
rightButton.selectedBg = generateBg(st::activeButtonBgOver);
rightButton.activeBg = generateBg(st::activeButtonFg);
return &(_rightButtons.emplace( return &(_rightButtons.emplace(
user->id, user->id,
std::move(rightButton)).first->second); std::move(rightButton)).first->second);
@ -1459,7 +1497,11 @@ void InnerWidget::paintPeerSearchResult(
context.st->photoSize); context.st->photoSize);
auto nameleft = context.st->nameLeft; auto nameleft = context.st->nameLeft;
auto namewidth = context.width - nameleft - context.st->padding.right(); auto available = context.width - nameleft - context.st->padding.right();
auto namewidth = available;
if (const auto used = Ui::PaintRightButton(p, context)) {
namewidth -= used - st::dialogsUnreadPadding;
}
QRect rectForName(nameleft, context.st->nameTop, namewidth, st::semiboldFont->height); QRect rectForName(nameleft, context.st->nameTop, namewidth, st::semiboldFont->height);
if (result->name.isEmpty()) { if (result->name.isEmpty()) {
@ -1611,7 +1653,7 @@ void InnerWidget::clearIrrelevantState() {
_filteredSelected = -1; _filteredSelected = -1;
setFilteredPressed(-1, false, false); setFilteredPressed(-1, false, false);
_peerSearchSelected = -1; _peerSearchSelected = -1;
setPeerSearchPressed(-1); setPeerSearchPressed(-1, false);
_previewSelected = -1; _previewSelected = -1;
setPreviewPressed(-1); setPreviewPressed(-1);
_searchedSelected = -1; _searchedSelected = -1;
@ -1630,20 +1672,28 @@ bool InnerWidget::lookupIsInBotAppButton(
if (const auto user = MaybeBotWithApp(row)) { if (const auto user = MaybeBotWithApp(row)) {
const auto it = _rightButtons.find(user->id); const auto it = _rightButtons.find(user->id);
if (it != _rightButtons.end()) { if (it != _rightButtons.end()) {
const auto s = it->second.bg.size() / style::DevicePixelRatio(); return lookupIsInRightButton(it->second, localPosition);
const auto r = QRect(
width() - s.width() - st::dialogRowOpenBotRight,
st::dialogRowOpenBotTop,
s.width(),
s.height());
if (r.contains(localPosition)) {
return true;
}
} }
} }
return false; return false;
} }
bool InnerWidget::lookupIsInRightButton(
const RightButton &button,
QPoint localPosition) {
if (!button.st) {
return false;
}
const auto s = button.bg.size() / style::DevicePixelRatio();
const auto r = QRect(
width() - s.width() - button.st->margin.right(),
button.st->margin.top(),
s.width(),
s.height());
return r.contains(localPosition);
}
void InnerWidget::selectByMouse(QPoint globalPosition) { void InnerWidget::selectByMouse(QPoint globalPosition) {
const auto local = mapFromGlobal(globalPosition); const auto local = mapFromGlobal(globalPosition);
if (updateReorderPinned(local)) { if (updateReorderPinned(local)) {
@ -1687,16 +1737,16 @@ void InnerWidget::selectByMouse(QPoint globalPosition) {
const auto mappedY = selected ? mouseY - offset - selected->top() : 0; const auto mappedY = selected ? mouseY - offset - selected->top() : 0;
const auto selectedTopicJump = selected const auto selectedTopicJump = selected
&& selected->lookupIsInTopicJump(local.x(), mappedY); && selected->lookupIsInTopicJump(local.x(), mappedY);
const auto selectedBotApp = selected const auto selectedRightButton = selected
&& lookupIsInBotAppButton(selected, QPoint(local.x(), mappedY)); && lookupIsInBotAppButton(selected, QPoint(local.x(), mappedY));
if (_collapsedSelected != collapsedSelected if (_collapsedSelected != collapsedSelected
|| _selected != selected || _selected != selected
|| _selectedTopicJump != selectedTopicJump || _selectedTopicJump != selectedTopicJump
|| _selectedBotApp != selectedBotApp) { || _selectedRightButton != selectedRightButton) {
updateSelectedRow(); updateSelectedRow();
_selected = selected; _selected = selected;
_selectedTopicJump = selectedTopicJump; _selectedTopicJump = selectedTopicJump;
_selectedBotApp = selectedBotApp; _selectedRightButton = selectedRightButton;
_collapsedSelected = collapsedSelected; _collapsedSelected = collapsedSelected;
updateSelectedRow(); updateSelectedRow();
setCursor((_selected || _collapsedSelected >= 0) setCursor((_selected || _collapsedSelected >= 0)
@ -1736,29 +1786,39 @@ void InnerWidget::selectByMouse(QPoint globalPosition) {
&& _filterResults[filteredSelected].row->lookupIsInTopicJump( && _filterResults[filteredSelected].row->lookupIsInTopicJump(
local.x(), local.x(),
mappedY); mappedY);
const auto selectedBotApp = (filteredSelected >= 0) const auto selectedRightButton = (filteredSelected >= 0)
&& lookupIsInBotAppButton( && lookupIsInBotAppButton(
_filterResults[filteredSelected].row, _filterResults[filteredSelected].row,
QPoint(local.x(), mappedY)); QPoint(local.x(), mappedY));
if (_filteredSelected != filteredSelected if (_filteredSelected != filteredSelected
|| _selectedTopicJump != selectedTopicJump || _selectedTopicJump != selectedTopicJump
|| _selectedBotApp != selectedBotApp) { || _selectedRightButton != selectedRightButton) {
updateSelectedRow(); updateSelectedRow();
_filteredSelected = filteredSelected; _filteredSelected = filteredSelected;
_selectedTopicJump = selectedTopicJump; _selectedTopicJump = selectedTopicJump;
_selectedBotApp = selectedBotApp; _selectedRightButton = selectedRightButton;
updateSelectedRow(); updateSelectedRow();
} }
} }
if (!_peerSearchResults.empty()) { if (!_peerSearchResults.empty()) {
auto skip = peerSearchOffset(); const auto skip = peerSearchOffset();
auto peerSearchSelected = (mouseY >= skip) ? ((mouseY - skip) / st::dialogsRowHeight) : -1; auto peerSearchSelected = (mouseY >= skip) ? ((mouseY - skip) / st::dialogsRowHeight) : -1;
if (peerSearchSelected < 0 || peerSearchSelected >= _peerSearchResults.size()) { if (peerSearchSelected < 0 || peerSearchSelected >= _peerSearchResults.size()) {
peerSearchSelected = -1; peerSearchSelected = -1;
} }
if (_peerSearchSelected != peerSearchSelected) { const auto mappedY = (peerSearchSelected >= 0)
? mouseY - skip - (peerSearchSelected * st::dialogsRowHeight)
: 0;
const auto selectedRightButton = (peerSearchSelected >= 0)
&& _peerSearchResults[peerSearchSelected]->sponsored
&& lookupIsInRightButton(
_peerSearchResults[peerSearchSelected]->sponsored->button,
QPoint(local.x(), mappedY));
if (_peerSearchSelected != peerSearchSelected
|| _selectedRightButton != selectedRightButton) {
updateSelectedRow(); updateSelectedRow();
_peerSearchSelected = peerSearchSelected; _peerSearchSelected = peerSearchSelected;
_selectedRightButton = selectedRightButton;
updateSelectedRow(); updateSelectedRow();
} }
} }
@ -1845,12 +1905,15 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
selectByMouse(e->globalPos()); selectByMouse(e->globalPos());
_pressButton = e->button(); _pressButton = e->button();
setPressed(_selected, _selectedTopicJump, _selectedBotApp); setPressed(_selected, _selectedTopicJump, _selectedRightButton);
setCollapsedPressed(_collapsedSelected); setCollapsedPressed(_collapsedSelected);
setHashtagPressed(_hashtagSelected); setHashtagPressed(_hashtagSelected);
_hashtagDeletePressed = _hashtagDeleteSelected; _hashtagDeletePressed = _hashtagDeleteSelected;
setFilteredPressed(_filteredSelected, _selectedTopicJump, _selectedBotApp); setFilteredPressed(
setPeerSearchPressed(_peerSearchSelected); _filteredSelected,
_selectedTopicJump,
_selectedRightButton);
setPeerSearchPressed(_peerSearchSelected, _selectedRightButton);
setPreviewPressed(_previewSelected); setPreviewPressed(_previewSelected);
setSearchedPressed(_searchedSelected); setSearchedPressed(_searchedSelected);
_pressedMorePosts = _selectedMorePosts; _pressedMorePosts = _selectedMorePosts;
@ -1881,7 +1944,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
- QPoint(0, dialogsOffset() + _pressed->top()); - QPoint(0, dialogsOffset() + _pressed->top());
if ((_pressButton == Qt::MiddleButton) if ((_pressButton == Qt::MiddleButton)
&& addQuickActionRipple(row, updateCallback)) { && addQuickActionRipple(row, updateCallback)) {
} else if (addBotAppRipple(origin, updateCallback)) { } else if (addRightButtonRipple(origin, updateCallback)) {
} else if (_pressedTopicJump) { } else if (_pressedTopicJump) {
row->addTopicJumpRipple( row->addTopicJumpRipple(
origin, origin,
@ -1908,7 +1971,7 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
const auto origin = e->pos() const auto origin = e->pos()
- QPoint(0, filteredOffset() + result.top); - QPoint(0, filteredOffset() + result.top);
const auto updateCallback = [=] { repaintDialogRow(filterId, row); }; const auto updateCallback = [=] { repaintDialogRow(filterId, row); };
if (addBotAppRipple(origin, updateCallback)) { if (addRightButtonRipple(origin, updateCallback)) {
} else if (_pressedTopicJump) { } else if (_pressedTopicJump) {
row->addTopicJumpRipple( row->addTopicJumpRipple(
origin, origin,
@ -1923,11 +1986,19 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
} }
} else if (base::in_range(_peerSearchPressed, 0, _peerSearchResults.size())) { } else if (base::in_range(_peerSearchPressed, 0, _peerSearchResults.size())) {
auto &result = _peerSearchResults[_peerSearchPressed]; auto &result = _peerSearchResults[_peerSearchPressed];
auto row = &result->row; const auto row = &result->row;
row->addRipple( const auto origin = e->pos()
e->pos() - QPoint(0, peerSearchOffset() + _peerSearchPressed * st::dialogsRowHeight), - QPoint(0, peerSearchOffset() + _peerSearchPressed * st::dialogsRowHeight);
QSize(width(), st::dialogsRowHeight), const auto updateCallback = [this, peer = result->peer] {
[this, peer = result->peer] { updateSearchResult(peer); }); updateSearchResult(peer);
};
if (addRightButtonRipple(origin, updateCallback)) {
} else {
row->addRipple(
origin,
QSize(width(), st::dialogsRowHeight),
updateCallback);
}
} else if (base::in_range(_searchedPressed, 0, _searchResults.size())) { } else if (base::in_range(_searchedPressed, 0, _searchResults.size())) {
auto &row = _searchResults[_searchedPressed]; auto &row = _searchResults[_searchedPressed];
row->addRipple( row->addRipple(
@ -1943,22 +2014,22 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
} }
} }
bool InnerWidget::addBotAppRipple(QPoint origin, Fn<void()> updateCallback) { bool InnerWidget::addRightButtonRipple(QPoint origin, Fn<void()> updateCallback) {
if (!(_pressedBotApp && _pressedBotAppData)) { if (!(_pressedRightButton && _pressedRightButtonData)) {
return false; return false;
} }
const auto size = _pressedBotAppData->bg.size() const auto size = _pressedRightButtonData->bg.size()
/ style::DevicePixelRatio(); / style::DevicePixelRatio();
if (!_pressedBotAppData->ripple) { if (!_pressedRightButtonData->ripple) {
_pressedBotAppData->ripple = std::make_unique<Ui::RippleAnimation>( _pressedRightButtonData->ripple = std::make_unique<Ui::RippleAnimation>(
st::defaultRippleAnimation, _pressedRightButtonData->st->button.ripple,
Ui::RippleAnimation::RoundRectMask(size, size.height() / 2), Ui::RippleAnimation::RoundRectMask(size, size.height() / 2),
std::move(updateCallback)); std::move(updateCallback));
} }
const auto shift = QPoint( const auto shift = QPoint(
width() - size.width() - st::dialogRowOpenBotRight, width() - size.width() - _pressedRightButtonData->st->margin.right(),
st::dialogRowOpenBotTop); _pressedRightButtonData->st->margin.top());
_pressedBotAppData->ripple->add(origin - shift); _pressedRightButtonData->ripple->add(origin - shift);
return true; return true;
} }
@ -2051,7 +2122,7 @@ void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
if (!_pressed if (!_pressed
|| _dragging || _dragging
|| (_state != WidgetState::Default) || (_state != WidgetState::Default)
|| _pressedBotApp) { || _pressedRightButtonData) {
return; return;
} else if (qAbs(localPosition.y() - _dragStart.y()) } else if (qAbs(localPosition.y() - _dragStart.y())
< style::ConvertScale(kStartReorderThreshold)) { < style::ConvertScale(kStartReorderThreshold)) {
@ -2321,7 +2392,7 @@ void InnerWidget::mousePressReleased(
setCollapsedPressed(-1); setCollapsedPressed(-1);
const auto pressedTopicRootId = _pressedTopicJumpRootId; const auto pressedTopicRootId = _pressedTopicJumpRootId;
const auto pressedTopicJump = _pressedTopicJump; const auto pressedTopicJump = _pressedTopicJump;
const auto pressedBotApp = _pressedBotApp; const auto pressedRightButton = _pressedRightButton;
auto pressed = _pressed; auto pressed = _pressed;
clearPressed(); clearPressed();
auto hashtagPressed = _hashtagPressed; auto hashtagPressed = _hashtagPressed;
@ -2331,7 +2402,7 @@ void InnerWidget::mousePressReleased(
auto filteredPressed = _filteredPressed; auto filteredPressed = _filteredPressed;
setFilteredPressed(-1, false, false); setFilteredPressed(-1, false, false);
auto peerSearchPressed = _peerSearchPressed; auto peerSearchPressed = _peerSearchPressed;
setPeerSearchPressed(-1); setPeerSearchPressed(-1, false);
auto previewPressed = _previewPressed; auto previewPressed = _previewPressed;
setPreviewPressed(-1); setPreviewPressed(-1);
auto searchedPressed = _searchedPressed; auto searchedPressed = _searchedPressed;
@ -2343,8 +2414,8 @@ void InnerWidget::mousePressReleased(
if (wasDragging) { if (wasDragging) {
selectByMouse(globalPosition); selectByMouse(globalPosition);
} }
if (_pressedBotAppData && _pressedBotAppData->ripple) { if (_pressedRightButtonData && _pressedRightButtonData->ripple) {
_pressedBotAppData->ripple->lastStop(); _pressedRightButtonData->ripple->lastStop();
} }
if (_activeQuickAction && pressed && !_activeQuickAction->data) { if (_activeQuickAction && pressed && !_activeQuickAction->data) {
if (const auto history = pressed->history()) { if (const auto history = pressed->history()) {
@ -2372,13 +2443,14 @@ void InnerWidget::mousePressReleased(
|| (pressed || (pressed
&& pressed == _selected && pressed == _selected
&& pressedTopicJump == _selectedTopicJump && pressedTopicJump == _selectedTopicJump
&& pressedBotApp == _selectedBotApp) && pressedRightButton == _selectedRightButton)
|| (hashtagPressed >= 0 || (hashtagPressed >= 0
&& hashtagPressed == _hashtagSelected && hashtagPressed == _hashtagSelected
&& hashtagDeletePressed == _hashtagDeleteSelected) && hashtagDeletePressed == _hashtagDeleteSelected)
|| (filteredPressed >= 0 && filteredPressed == _filteredSelected) || (filteredPressed >= 0 && filteredPressed == _filteredSelected)
|| (peerSearchPressed >= 0 || (peerSearchPressed >= 0
&& peerSearchPressed == _peerSearchSelected) && peerSearchPressed == _peerSearchSelected
&& pressedRightButton == _selectedRightButton)
|| (previewPressed >= 0 || (previewPressed >= 0
&& previewPressed == _previewSelected) && previewPressed == _previewSelected)
|| (searchedPressed >= 0 || (searchedPressed >= 0
@ -2387,13 +2459,15 @@ void InnerWidget::mousePressReleased(
&& pressedMorePosts == _selectedMorePosts) && pressedMorePosts == _selectedMorePosts)
|| (pressedChatTypeFilter || (pressedChatTypeFilter
&& pressedChatTypeFilter == _selectedChatTypeFilter)) { && pressedChatTypeFilter == _selectedChatTypeFilter)) {
if (pressedBotApp && (pressed || filteredPressed >= 0)) { if (pressedRightButton && (pressed || filteredPressed >= 0)) {
const auto &row = pressed const auto &row = pressed
? pressed ? pressed
: _filterResults[filteredPressed].row.get(); : _filterResults[filteredPressed].row.get();
if (const auto user = MaybeBotWithApp(row)) { if (const auto user = MaybeBotWithApp(row)) {
_openBotMainAppRequests.fire(peerToUser(user->id)); _openBotMainAppRequests.fire(peerToUser(user->id));
} }
} else if (pressedRightButton && peerSearchPressed >= 0) {
showSponsoredMenu(peerSearchPressed, globalPosition);
} else { } else {
chooseRow(modifiers, pressedTopicRootId); chooseRow(modifiers, pressedTopicRootId);
} }
@ -2420,25 +2494,25 @@ void InnerWidget::setCollapsedPressed(int pressed) {
void InnerWidget::setPressed( void InnerWidget::setPressed(
Row *pressed, Row *pressed,
bool pressedTopicJump, bool pressedTopicJump,
bool pressedBotApp) { bool pressedRightButton) {
if ((_pressed != pressed) if ((_pressed != pressed)
|| (pressed && _pressedTopicJump != pressedTopicJump) || (pressed && _pressedTopicJump != pressedTopicJump)
|| (pressed && _pressedBotApp != pressedBotApp)) { || (pressed && _pressedRightButton != pressedRightButton)) {
if (_pressed) { if (_pressed) {
_pressed->stopLastRipple(); _pressed->stopLastRipple();
} }
if (_pressedBotAppData && _pressedBotAppData->ripple) { if (_pressedRightButtonData && _pressedRightButtonData->ripple) {
_pressedBotAppData->ripple->lastStop(); _pressedRightButtonData->ripple->lastStop();
} }
_pressed = pressed; _pressed = pressed;
if (pressed || !pressedTopicJump || !pressedBotApp) { if (pressed || !pressedTopicJump || !pressedRightButton) {
_pressedTopicJump = pressedTopicJump; _pressedTopicJump = pressedTopicJump;
_pressedBotApp = pressedBotApp; _pressedRightButton = pressedRightButton;
if (pressedBotApp) { if (pressedRightButton) {
if (const auto user = MaybeBotWithApp(pressed)) { if (const auto user = MaybeBotWithApp(pressed)) {
const auto it = _rightButtons.find(user->id); const auto it = _rightButtons.find(user->id);
if (it != _rightButtons.end()) { if (it != _rightButtons.end()) {
_pressedBotAppData = &(it->second); _pressedRightButtonData = &(it->second);
} }
} }
} }
@ -2465,26 +2539,26 @@ void InnerWidget::setHashtagPressed(int pressed) {
void InnerWidget::setFilteredPressed( void InnerWidget::setFilteredPressed(
int pressed, int pressed,
bool pressedTopicJump, bool pressedTopicJump,
bool pressedBotApp) { bool pressedRightButton) {
if (_filteredPressed != pressed if (_filteredPressed != pressed
|| (pressed >= 0 && _pressedTopicJump != pressedTopicJump) || (pressed >= 0 && _pressedTopicJump != pressedTopicJump)
|| (pressed >= 0 && _pressedBotApp != pressedBotApp)) { || (pressed >= 0 && _pressedRightButton != pressedRightButton)) {
if (base::in_range(_filteredPressed, 0, _filterResults.size())) { if (base::in_range(_filteredPressed, 0, _filterResults.size())) {
_filterResults[_filteredPressed].row->stopLastRipple(); _filterResults[_filteredPressed].row->stopLastRipple();
} }
if (_pressedBotAppData && _pressedBotAppData->ripple) { if (_pressedRightButtonData && _pressedRightButtonData->ripple) {
_pressedBotAppData->ripple->lastStop(); _pressedRightButtonData->ripple->lastStop();
} }
_filteredPressed = pressed; _filteredPressed = pressed;
if (pressed >= 0 || !pressedTopicJump || !pressedBotApp) { if (pressed >= 0 || !pressedTopicJump || !pressedRightButton) {
_pressedTopicJump = pressedTopicJump; _pressedTopicJump = pressedTopicJump;
_pressedBotApp = pressedBotApp; _pressedRightButton = pressedRightButton;
if (pressed >= 0 && pressedBotApp) { if (pressed >= 0 && pressedRightButton) {
const auto &row = _filterResults[pressed].row; const auto &row = _filterResults[pressed].row;
if (const auto history = row->history()) { if (const auto history = row->history()) {
const auto it = _rightButtons.find(history->peer->id); const auto it = _rightButtons.find(history->peer->id);
if (it != _rightButtons.end()) { if (it != _rightButtons.end()) {
_pressedBotAppData = &(it->second); _pressedRightButtonData = &(it->second);
} }
} }
} }
@ -2497,11 +2571,26 @@ void InnerWidget::setFilteredPressed(
} }
} }
void InnerWidget::setPeerSearchPressed(int pressed) { void InnerWidget::setPeerSearchPressed(int pressed, bool pressedRightButton) {
if (base::in_range(_peerSearchPressed, 0, _peerSearchResults.size())) { if (_peerSearchPressed != pressed
_peerSearchResults[_peerSearchPressed]->row.stopLastRipple(); || (pressed >= 0 && _pressedRightButton != pressedRightButton)) {
if (base::in_range(_peerSearchPressed, 0, _peerSearchResults.size())) {
_peerSearchResults[_peerSearchPressed]->row.stopLastRipple();
}
if (_pressedRightButtonData && _pressedRightButtonData->ripple) {
_pressedRightButtonData->ripple->lastStop();
}
_peerSearchPressed = pressed;
if (pressed >= 0 || !pressedRightButton) {
_pressedRightButton = pressedRightButton;
if (pressed >= 0 && pressedRightButton) {
const auto &entry = _peerSearchResults[pressed];
if (entry->sponsored) {
_pressedRightButtonData = &entry->sponsored->button;
}
}
}
} }
_peerSearchPressed = pressed;
} }
void InnerWidget::setPreviewPressed(int pressed) { void InnerWidget::setPreviewPressed(int pressed) {
@ -2564,7 +2653,7 @@ void InnerWidget::dialogRowReplaced(
_selected = newRow; _selected = newRow;
} }
if (_pressed == oldRow) { if (_pressed == oldRow) {
setPressed(newRow, _pressedTopicJump, _pressedBotApp); setPressed(newRow, _pressedTopicJump, _pressedRightButton);
} }
if (_dragging == oldRow) { if (_dragging == oldRow) {
if (newRow) { if (newRow) {
@ -3081,6 +3170,62 @@ void InnerWidget::contextMenuEvent(QContextMenuEvent *e) {
} }
} }
void InnerWidget::showSponsoredMenu(int peerSearchIndex, QPoint globalPos) {
_menu = nullptr;
const auto count = int(_peerSearchResults.size());
const auto entry = (peerSearchIndex >= 0 && peerSearchIndex < count)
? _peerSearchResults[peerSearchIndex].get()
: nullptr;
if (!entry || !entry->sponsored) {
return;
}
_peerSearchMenu = peerSearchIndex;
_menu = base::make_unique_q<Ui::PopupMenu>(
this,
st::popupMenuExpandedSeparator);
const auto peer = entry->peer;
const auto remove = crl::guard(this, [=] {
_sponsoredRemoved.emplace(peer);
_peerSearchResults.erase(
ranges::remove(
_peerSearchResults,
peer,
&PeerSearchResult::peer),
end(_peerSearchResults));
refresh();
});
Menu::FillSponsored(
this,
Ui::Menu::CreateAddActionCallback(_menu),
_controller->uiShow(),
Menu::SponsoredPhrases::Search,
session().sponsoredMessages().lookupDetails(entry->sponsored->data),
session().sponsoredMessages().createReportCallback(
entry->sponsored->data.randomId,
remove),
false,
false);
QObject::connect(_menu.get(), &QObject::destroyed, [=] {
if (_peerSearchMenu >= 0
&& _peerSearchMenu < _peerSearchResults.size()) {
const auto index = std::exchange(_peerSearchMenu, -1);
updateSearchResult(_peerSearchResults[index]->peer);
}
const auto globalPosition = QCursor::pos();
if (rect().contains(mapFromGlobal(globalPosition))) {
setMouseTracking(true);
selectByMouse(globalPosition);
}
});
if (_menu->empty()) {
_menu = nullptr;
} else {
_menu->popup(globalPos);
}
}
void InnerWidget::parentGeometryChanged() { void InnerWidget::parentGeometryChanged() {
const auto globalPosition = QCursor::pos(); const auto globalPosition = QCursor::pos();
if (rect().contains(mapFromGlobal(globalPosition))) { if (rect().contains(mapFromGlobal(globalPosition))) {
@ -3362,14 +3507,23 @@ InnerWidget::~InnerWidget() {
clearSearchResults(); clearSearchResults();
} }
void InnerWidget::clearSearchResults(bool clearPeerSearchResults) { void InnerWidget::clearSearchResults(bool alsoPeerSearchResults) {
if (clearPeerSearchResults) { if (alsoPeerSearchResults) {
_peerSearchResults.clear(); clearPeerSearchResults();
} }
_searchResults.clear(); _searchResults.clear();
_searchedCount = _searchedMigratedCount = 0; _searchedCount = _searchedMigratedCount = 0;
} }
void InnerWidget::clearPeerSearchResults() {
_peerSearchResults.clear();
if (_pressedRightButtonSponsored) {
_pressedRightButtonData = nullptr;
_pressedRightButtonSponsored = false;
_pressedRightButton = false;
}
}
void InnerWidget::clearPreviewResults() { void InnerWidget::clearPreviewResults() {
_previewResults.clear(); _previewResults.clear();
_previewCount = 0; _previewCount = 0;
@ -3691,7 +3845,7 @@ void InnerWidget::peerSearchReceived(Api::PeerSearchResult result) {
} }
_peerSearchQuery = result.query.toLower().trimmed(); _peerSearchQuery = result.query.toLower().trimmed();
_peerSearchResults.clear(); clearPeerSearchResults();
_peerSearchResults.reserve(result.peers.size() _peerSearchResults.reserve(result.peers.size()
+ result.sponsored.size()); + result.sponsored.size());
for (const auto &peer : result.my) { for (const auto &peer : result.my) {
@ -3706,14 +3860,17 @@ void InnerWidget::peerSearchReceived(Api::PeerSearchResult result) {
}; };
auto added = base::flat_set<not_null<PeerData*>>(); auto added = base::flat_set<not_null<PeerData*>>();
for (const auto &sponsored : result.sponsored) { for (const auto &sponsored : result.sponsored) {
if (inlist(sponsored.peer)) { const auto peer = sponsored.peer;
if (inlist(peer) || _sponsoredRemoved.contains(peer)) {
continue; continue;
} }
_peerSearchResults.push_back( _peerSearchResults.push_back(
std::make_unique<PeerSearchResult>(sponsored.peer)); std::make_unique<PeerSearchResult>(peer));
_peerSearchResults.back()->sponsored _peerSearchResults.back()->sponsored
= std::make_unique<Api::SponsoredSearchResult>(sponsored); = std::make_unique<SponsoredSearchResult>(SponsoredSearchResult{
added.emplace(sponsored.peer); .data = sponsored,
});
added.emplace(peer);
} }
for (const auto &peer : result.peers) { for (const auto &peer : result.peers) {
if (added.contains(peer) || inlist(peer)) { if (added.contains(peer) || inlist(peer)) {
@ -4054,7 +4211,7 @@ void InnerWidget::clearFilter() {
_hashtagResults.clear(); _hashtagResults.clear();
_filterResults.clear(); _filterResults.clear();
_filterResultsGlobal.clear(); _filterResultsGlobal.clear();
_peerSearchResults.clear(); clearPeerSearchResults();
_searchResults.clear(); _searchResults.clear();
_previewResults.clear(); _previewResults.clear();
_trackedHistories.clear(); _trackedHistories.clear();
@ -4515,7 +4672,7 @@ ChosenRow InnerWidget::computeChosenRow() const {
.key = session().data().history(row->peer), .key = session().data().history(row->peer),
.message = Data::UnreadMessagePosition, .message = Data::UnreadMessagePosition,
.sponsoredRandomId = (row->sponsored .sponsoredRandomId = (row->sponsored
? row->sponsored->randomId ? row->sponsored->data.randomId
: QByteArray()), : QByteArray()),
}; };
} else if (base::in_range(_previewSelected, 0, _previewResults.size())) { } else if (base::in_range(_previewSelected, 0, _previewResults.size())) {

View file

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace style { namespace style {
struct DialogRow; struct DialogRow;
struct DialogRightButton;
} // namespace style } // namespace style
namespace Api { namespace Api {
@ -242,6 +243,7 @@ protected:
private: private:
struct CollapsedRow; struct CollapsedRow;
struct HashtagResult; struct HashtagResult;
struct SponsoredSearchResult;
struct PeerSearchResult; struct PeerSearchResult;
struct TagCache; struct TagCache;
@ -299,6 +301,7 @@ private:
void repaintDialogRow(RowDescriptor row); void repaintDialogRow(RowDescriptor row);
void refreshDialogRow(RowDescriptor row); void refreshDialogRow(RowDescriptor row);
bool updateEntryHeight(not_null<Entry*> entry); bool updateEntryHeight(not_null<Entry*> entry);
void showSponsoredMenu(int peerSearchIndex, QPoint globalPos);
void clearMouseSelection(bool clearSelection = false); void clearMouseSelection(bool clearSelection = false);
void mousePressReleased( void mousePressReleased(
@ -312,14 +315,17 @@ private:
void scrollToItem(int top, int height); void scrollToItem(int top, int height);
void scrollToDefaultSelected(); void scrollToDefaultSelected();
void setCollapsedPressed(int pressed); void setCollapsedPressed(int pressed);
void setPressed(Row *pressed, bool pressedTopicJump, bool pressedBotApp); void setPressed(
Row *pressed,
bool pressedTopicJump,
bool pressedRightButton);
void clearPressed(); void clearPressed();
void setHashtagPressed(int pressed); void setHashtagPressed(int pressed);
void setFilteredPressed( void setFilteredPressed(
int pressed, int pressed,
bool pressedTopicJump, bool pressedTopicJump,
bool pressedBotApp); bool pressedRightButton);
void setPeerSearchPressed(int pressed); void setPeerSearchPressed(int pressed, bool pressedRightButton);
void setPreviewPressed(int pressed); void setPreviewPressed(int pressed);
void setSearchedPressed(int pressed); void setSearchedPressed(int pressed);
bool isPressed() const { bool isPressed() const {
@ -357,6 +363,8 @@ private:
bool addBotAppRipple(QPoint origin, Fn<void()> updateCallback); bool addBotAppRipple(QPoint origin, Fn<void()> updateCallback);
bool addQuickActionRipple(not_null<Row*> row, Fn<void()> updateCallback); bool addQuickActionRipple(not_null<Row*> row, Fn<void()> updateCallback);
bool addRightButtonRipple(QPoint origin, Fn<void()> updateCallback);
void setupShortcuts(); void setupShortcuts();
RowDescriptor computeJump( RowDescriptor computeJump(
const RowDescriptor &to, const RowDescriptor &to,
@ -459,7 +467,8 @@ private:
Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history); Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);
Row *shownRowByKey(Key key); Row *shownRowByKey(Key key);
void clearSearchResults(bool clearPeerSearchResults = true); void clearSearchResults(bool alsoPeerSearchResults = true);
void clearPeerSearchResults();
void clearPreviewResults(); void clearPreviewResults();
void updateSelectedRow(Key key = Key()); void updateSelectedRow(Key key = Key());
void trackResultsHistory(not_null<History*> history); void trackResultsHistory(not_null<History*> history);
@ -493,7 +502,14 @@ private:
[[nodiscard]] bool lookupIsInBotAppButton( [[nodiscard]] bool lookupIsInBotAppButton(
Row *row, Row *row,
QPoint localPosition); QPoint localPosition);
[[nodiscard]] bool lookupIsInRightButton(
const RightButton &button,
QPoint localPosition);
[[nodiscard]] RightButton *maybeCacheRightButton(Row *row); [[nodiscard]] RightButton *maybeCacheRightButton(Row *row);
void fillRightButton(
RightButton &button,
const TextWithEntities &text,
const style::DialogRightButton &st);
[[nodiscard]] QImage *cacheChatsFilterTag( [[nodiscard]] QImage *cacheChatsFilterTag(
const Data::ChatFilter &filter, const Data::ChatFilter &filter,
@ -529,9 +545,10 @@ private:
bool _selectedTopicJump = false; bool _selectedTopicJump = false;
bool _pressedTopicJump = false; bool _pressedTopicJump = false;
RightButton *_pressedBotAppData = nullptr; RightButton *_pressedRightButtonData = nullptr;
bool _selectedBotApp = false; bool _pressedRightButtonSponsored = false;
bool _pressedBotApp = false; bool _selectedRightButton = false;
bool _pressedRightButton = false;
Row *_dragging = nullptr; Row *_dragging = nullptr;
int _draggingIndex = -1; int _draggingIndex = -1;
@ -566,9 +583,11 @@ private:
rpl::lifetime _trackedLifetime; rpl::lifetime _trackedLifetime;
QString _peerSearchQuery; QString _peerSearchQuery;
base::flat_set<not_null<PeerData*>> _sponsoredRemoved;
std::vector<std::unique_ptr<PeerSearchResult>> _peerSearchResults; std::vector<std::unique_ptr<PeerSearchResult>> _peerSearchResults;
int _peerSearchSelected = -1; int _peerSearchSelected = -1;
int _peerSearchPressed = -1; int _peerSearchPressed = -1;
int _peerSearchMenu = -1;
std::vector<std::unique_ptr<FakeRow>> _previewResults; std::vector<std::unique_ptr<FakeRow>> _previewResults;
int _previewCount = 0; int _previewCount = 0;

View file

@ -835,7 +835,10 @@ void Widget::setupSwipeBack() {
void Widget::chosenRow(const ChosenRow &row) { void Widget::chosenRow(const ChosenRow &row) {
storiesToggleExplicitExpand(false); storiesToggleExplicitExpand(false);
if (!_searchState.query.isEmpty()) { if (!row.sponsoredRandomId.isEmpty()) {
auto &messages = session().sponsoredMessages();
messages.clicked(row.sponsoredRandomId, false, false);
} else if (!_searchState.query.isEmpty()) {
if (const auto history = row.key.history()) { if (const auto history = row.key.history()) {
session().recentPeers().bump(history->peer); session().recentPeers().bump(history->peer);
} }
@ -846,11 +849,6 @@ void Widget::chosenRow(const ChosenRow &row) {
? history->peer->forumTopicFor(row.message.fullId.msg) ? history->peer->forumTopicFor(row.message.fullId.msg)
: nullptr; : nullptr;
if (!row.sponsoredRandomId.isEmpty()) {
auto &messages = session().sponsoredMessages();
messages.clicked(row.sponsoredRandomId, false, false);
}
if (topicJump) { if (topicJump) {
if (controller()->shownForum().current() == topicJump->forum()) { if (controller()->shownForum().current() == topicJump->forum()) {
controller()->closeForum(); controller()->closeForum();

View file

@ -125,16 +125,18 @@ void PaintRowTopRight(
text); text);
} }
int PaintRightButton(QPainter &p, const PaintContext &context) { int PaintRightButtonImpl(QPainter &p, const PaintContext &context) {
if (context.width < st::columnMinimalWidthLeft) { if (context.width < st::columnMinimalWidthLeft) {
return 0; return 0;
} }
if (const auto rightButton = context.rightButton) { if (const auto rightButton = context.rightButton) {
Assert(rightButton->st != nullptr);
const auto size = rightButton->bg.size() / style::DevicePixelRatio(); const auto size = rightButton->bg.size() / style::DevicePixelRatio();
const auto left = context.width const auto left = context.width
- size.width() - size.width()
- st::dialogRowOpenBotRight; - rightButton->st->margin.right();
const auto top = st::dialogRowOpenBotTop; const auto top = rightButton->st->margin.top();
p.drawImage( p.drawImage(
left, left,
top, top,
@ -149,22 +151,22 @@ int PaintRightButton(QPainter &p, const PaintContext &context) {
left, left,
top, top,
size.width() - size.height() / 2, size.width() - size.height() / 2,
context.active (context.active
? &st::universalRippleAnimation.color->c ? &st::universalRippleAnimation.color->c
: &st::activeButtonBgRipple->c); : &rightButton->st->button.ripple.color->c));
if (rightButton->ripple->empty()) { if (rightButton->ripple->empty()) {
rightButton->ripple.reset(); rightButton->ripple.reset();
} }
} }
p.setPen(context.active p.setPen(context.active
? st::activeButtonBg ? rightButton->st->button.textBg
: context.selected : context.selected
? st::activeButtonFgOver ? rightButton->st->button.textFgOver
: st::activeButtonFg); : rightButton->st->button.textFg);
rightButton->text.draw(p, { rightButton->text.draw(p, {
.position = QPoint( .position = QPoint(
left + size.height() / 2, left + size.height() / 2,
top + (st::dialogRowOpenBotHeight - rightButton->text.minHeight()) / 2), top + rightButton->st->button.textTop),
.outerWidth = size.width() - size.height() / 2, .outerWidth = size.width() - size.height() / 2,
.availableWidth = size.width() - size.height() / 2, .availableWidth = size.width() - size.height() / 2,
.elisionLines = 1, .elisionLines = 1,
@ -1285,4 +1287,8 @@ void PaintCollapsedRow(
} }
} }
int PaintRightButton(QPainter &p, const PaintContext &context) {
return PaintRightButtonImpl(p, context);
}
} // namespace Dialogs::Ui } // namespace Dialogs::Ui

View file

@ -110,4 +110,6 @@ void PaintCollapsedRow(
int unread, int unread,
const PaintContext &context); const PaintContext &context);
int PaintRightButton(QPainter &p, const PaintContext &context);
} // namespace Dialogs::Ui } // namespace Dialogs::Ui

View file

@ -204,7 +204,7 @@ RecentRow::RecentRow(not_null<PeerData*> peer)
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
if (user->botInfo && user->botInfo->hasMainApp) { if (user->botInfo && user->botInfo->hasMainApp) {
return std::make_unique<Ui::Text::String>( return std::make_unique<Ui::Text::String>(
st::dialogRowOpenBotTextStyle, st::dialogRowOpenBotRecent.button.style,
tr::lng_profile_open_app_short(tr::now)); tr::lng_profile_open_app_short(tr::now));
} }
} }
@ -275,20 +275,15 @@ QSize RecentRow::rightActionSize() const {
if (_mainAppText && _badgeSize.isEmpty()) { if (_mainAppText && _badgeSize.isEmpty()) {
return QSize( return QSize(
_mainAppText->maxWidth() + _mainAppText->minHeight(), _mainAppText->maxWidth() + _mainAppText->minHeight(),
st::dialogRowOpenBotHeight); st::dialogRowOpenBotRecent.button.height);
} }
return _badgeSize; return _badgeSize;
} }
QMargins RecentRow::rightActionMargins() const { QMargins RecentRow::rightActionMargins() const {
if (_mainAppText && _badgeSize.isEmpty()) { if (_mainAppText && _badgeSize.isEmpty()) {
return QMargins( return st::dialogRowOpenBotRecent.margin;
0, } else if (_badgeSize.isEmpty()) {
st::dialogRowOpenBotRecentTop,
st::dialogRowOpenBotRight,
0);
}
if (_badgeSize.isEmpty()) {
return {}; return {};
} }
const auto x = st::recentPeersItem.photoPosition.x(); const auto x = st::recentPeersItem.photoPosition.x();
@ -321,8 +316,7 @@ void RecentRow::rightActionPaint(
p.setPen(actionSelected p.setPen(actionSelected
? st::activeButtonFgOver ? st::activeButtonFgOver
: st::activeButtonFg); : st::activeButtonFg);
const auto top = 0 const auto top = st::dialogRowOpenBotRecent.button.textTop;
+ (st::dialogRowOpenBotHeight - _mainAppText->minHeight()) / 2;
_mainAppText->draw(p, { _mainAppText->draw(p, {
.position = QPoint(x + size.height() / 2, y + top), .position = QPoint(x + size.height() / 2, y + top),
.outerWidth = outerWidth, .outerWidth = outerWidth,

View file

@ -699,7 +699,8 @@ ClickHandlerPtr HideSponsoredClickHandler() {
if (session.premium()) { if (session.premium()) {
using Result = Data::SponsoredReportResult; using Result = Data::SponsoredReportResult;
session.sponsoredMessages().createReportCallback( session.sponsoredMessages().createReportCallback(
my.itemId)(Result::Id("-1"), [](const auto &) {}); my.itemId
).callback(Result::Id("-1"), [](const auto &) {});
} else { } else {
ShowPremiumPreviewBox(controller, PremiumFeature::NoAds); ShowPremiumPreviewBox(controller, PremiumFeature::NoAds);
} }

View file

@ -116,7 +116,7 @@ channelEarnFadeDuration: 60;
channelEarnLearnDescription: FlatLabel(defaultFlatLabel) { channelEarnLearnDescription: FlatLabel(defaultFlatLabel) {
maxHeight: 0px; maxHeight: 0px;
minWidth: 280px; minWidth: 264px;
align: align(top); align: align(top);
} }

View file

@ -42,15 +42,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Menu { namespace Menu {
namespace { namespace {
[[nodiscard]] SponsoredPhrases PhrasesForMessage(FullMsgId fullId) {
return peerIsChannel(fullId.peer)
? SponsoredPhrases::Channel
: SponsoredPhrases::Bot;
}
void AboutBox( void AboutBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
const FullMsgId &fullId) { SponsoredPhrases phrases,
const Data::SponsoredMessages::Details &details,
Data::SponsoredReportAction report) {
constexpr auto kUrl = "https://promote.telegram.org"_cs; constexpr auto kUrl = "https://promote.telegram.org"_cs;
box->setNoContentMargin(true); box->setWidth(st::boxWideWidth);
const auto isChannel = peerIsChannel(fullId.peer); const auto isChannel = (phrases == SponsoredPhrases::Channel);
const auto isSearch = (phrases == SponsoredPhrases::Search);
const auto session = &show->session(); const auto session = &show->session();
const auto content = box->verticalLayout().get(); const auto content = box->verticalLayout().get();
@ -138,20 +147,24 @@ void AboutBox(
tr::lng_sponsored_revenued_info1_title(), tr::lng_sponsored_revenued_info1_title(),
(isChannel (isChannel
? tr::lng_sponsored_revenued_info1_description ? tr::lng_sponsored_revenued_info1_description
: isSearch
? tr::lng_sponsored_revenued_info1_search_description
: tr::lng_sponsored_revenued_info1_bot_description)( : tr::lng_sponsored_revenued_info1_bot_description)(
Ui::Text::RichLangValue), Ui::Text::RichLangValue),
st::sponsoredAboutPrivacyIcon); st::sponsoredAboutPrivacyIcon);
Ui::AddSkip(content); if (!isSearch) {
Ui::AddSkip(content); Ui::AddSkip(content);
addEntry( Ui::AddSkip(content);
(isChannel addEntry(
? tr::lng_sponsored_revenued_info2_title (isChannel
: tr::lng_sponsored_revenued_info2_bot_title)(), ? tr::lng_sponsored_revenued_info2_title
(isChannel : tr::lng_sponsored_revenued_info2_bot_title)(),
? tr::lng_sponsored_revenued_info2_description (isChannel
: tr::lng_sponsored_revenued_info2_bot_description)( ? tr::lng_sponsored_revenued_info2_description
Ui::Text::RichLangValue), : tr::lng_sponsored_revenued_info2_bot_description)(
st::sponsoredAboutSplitIcon); Ui::Text::RichLangValue),
st::sponsoredAboutSplitIcon);
}
Ui::AddSkip(content); Ui::AddSkip(content);
Ui::AddSkip(content); Ui::AddSkip(content);
auto link = tr::lng_settings_privacy_premium_link( auto link = tr::lng_settings_privacy_premium_link(
@ -160,17 +173,32 @@ void AboutBox(
}); });
addEntry( addEntry(
tr::lng_sponsored_revenued_info3_title(), tr::lng_sponsored_revenued_info3_title(),
isChannel (isChannel
? tr::lng_sponsored_revenued_info3_description( ? tr::lng_sponsored_revenued_info3_description(
lt_count, lt_count,
rpl::single(float64(levels)), rpl::single(float64(levels)),
lt_link, lt_link,
std::move(link), std::move(link),
Ui::Text::RichLangValue) Ui::Text::RichLangValue)
: isSearch
? tr::lng_sponsored_revenued_info3_search_description(
lt_link,
tr::lng_sponsored_revenued_info3_search_link(
lt_arrow,
rpl::single(
Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
Ui::Text::WithEntities
) | rpl::map([](TextWithEntities &&link) {
return Ui::Text::Wrapped(
std::move(link),
EntityType::CustomUrl,
u"internal:"_q);
}),
Ui::Text::RichLangValue)
: tr::lng_sponsored_revenued_info3_bot_description( : tr::lng_sponsored_revenued_info3_bot_description(
lt_link, lt_link,
std::move(link), std::move(link),
Ui::Text::RichLangValue), Ui::Text::RichLangValue)),
st::sponsoredAboutRemoveIcon)->setClickHandlerFilter([=]( st::sponsoredAboutRemoveIcon)->setClickHandlerFilter([=](
const auto &...) { const auto &...) {
ShowPremiumPreviewBox(show, PremiumFeature::NoAds); ShowPremiumPreviewBox(show, PremiumFeature::NoAds);
@ -200,6 +228,8 @@ void AboutBox(
content, content,
(isChannel (isChannel
? tr::lng_sponsored_revenued_footer_description ? tr::lng_sponsored_revenued_footer_description
: isSearch
? tr::lng_sponsored_revenued_footer_search_description
: tr::lng_sponsored_revenued_footer_bot_description)( : tr::lng_sponsored_revenued_footer_bot_description)(
lt_link, lt_link,
tr::lng_channel_earn_about_link( tr::lng_channel_earn_about_link(
@ -256,7 +286,9 @@ void AboutBox(
top, top,
Ui::Menu::CreateAddActionCallback(menu->get()), Ui::Menu::CreateAddActionCallback(menu->get()),
show, show,
fullId, phrases,
details,
report,
false, false,
true); true);
const auto global = top->mapToGlobal( const auto global = top->mapToGlobal(
@ -269,14 +301,11 @@ void AboutBox(
return true; return true;
}); });
} }
} }
void ShowReportSponsoredBox( void ShowReportSponsoredBox(
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
const FullMsgId &fullId) { Data::SponsoredReportAction report) {
auto &sponsoredMessages = show->session().sponsoredMessages();
const auto report = sponsoredMessages.createReportCallback(fullId);
const auto guideLink = Ui::Text::Link( const auto guideLink = Ui::Text::Link(
tr::lng_report_sponsored_reported_link(tr::now), tr::lng_report_sponsored_reported_link(tr::now),
u"https://promote.telegram.org/guidelines"_q); u"https://promote.telegram.org/guidelines"_q);
@ -284,7 +313,7 @@ void ShowReportSponsoredBox(
auto performRequest = [=]( auto performRequest = [=](
const auto &repeatRequest, const auto &repeatRequest,
Data::SponsoredReportResult::Id id) -> void { Data::SponsoredReportResult::Id id) -> void {
report(id, [=](const Data::SponsoredReportResult &result) { report.callback(id, [=](const Data::SponsoredReportResult &result) {
if (!result.error.isEmpty()) { if (!result.error.isEmpty()) {
show->showToast(result.error); show->showToast(result.error);
} }
@ -360,11 +389,12 @@ void FillSponsored(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
const Ui::Menu::MenuCallback &addAction, const Ui::Menu::MenuCallback &addAction,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
const FullMsgId &fullId, SponsoredPhrases phrases,
const Data::SponsoredMessages::Details &details,
Data::SponsoredReportAction report,
bool mediaViewer, bool mediaViewer,
bool skipAbout) { bool skipAbout) {
const auto session = &show->session(); const auto session = &show->session();
const auto details = session->sponsoredMessages().lookupDetails(fullId);
const auto &info = details.info; const auto &info = details.info;
if (!mediaViewer && !info.empty()) { if (!mediaViewer && !info.empty()) {
@ -408,12 +438,12 @@ void FillSponsored(
if (details.canReport) { if (details.canReport) {
if (!skipAbout) { if (!skipAbout) {
addAction(tr::lng_sponsored_menu_revenued_about(tr::now), [=] { addAction(tr::lng_sponsored_menu_revenued_about(tr::now), [=] {
show->show(Box(AboutBox, show, fullId)); show->show(Box(AboutBox, show, phrases, details, report));
}, (mediaViewer ? &st::mediaMenuIconInfo : &st::menuIconInfo)); }, (mediaViewer ? &st::mediaMenuIconInfo : &st::menuIconInfo));
} }
addAction(tr::lng_sponsored_menu_revenued_report(tr::now), [=] { addAction(tr::lng_sponsored_menu_revenued_report(tr::now), [=] {
ShowReportSponsoredBox(show, fullId); ShowReportSponsoredBox(show, report);
}, (mediaViewer ? &st::mediaMenuIconBlock : &st::menuIconBlock)); }, (mediaViewer ? &st::mediaMenuIconBlock : &st::menuIconBlock));
addAction({ addAction({
@ -426,14 +456,32 @@ void FillSponsored(
addAction(tr::lng_sponsored_hide_ads(tr::now), [=] { addAction(tr::lng_sponsored_hide_ads(tr::now), [=] {
if (session->premium()) { if (session->premium()) {
using Result = Data::SponsoredReportResult; using Result = Data::SponsoredReportResult;
session->sponsoredMessages().createReportCallback( report.callback(Result::Id("-1"), [](const auto &) {});
fullId)(Result::Id("-1"), [](const auto &) {});
} else { } else {
ShowPremiumPreviewBox(show, PremiumFeature::NoAds); ShowPremiumPreviewBox(show, PremiumFeature::NoAds);
} }
}, (mediaViewer ? &st::mediaMenuIconCancel : &st::menuIconCancel)); }, (mediaViewer ? &st::mediaMenuIconCancel : &st::menuIconCancel));
} }
void FillSponsored(
not_null<Ui::RpWidget*> parent,
const Ui::Menu::MenuCallback &addAction,
std::shared_ptr<ChatHelpers::Show> show,
const FullMsgId &fullId,
bool mediaViewer,
bool skipAbout) {
const auto session = &show->session();
FillSponsored(
parent,
addAction,
show,
PhrasesForMessage(fullId),
session->sponsoredMessages().lookupDetails(fullId),
session->sponsoredMessages().createReportCallback(fullId),
mediaViewer,
skipAbout);
}
void ShowSponsored( void ShowSponsored(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
@ -455,8 +503,14 @@ void ShowSponsored(
void ShowSponsoredAbout( void ShowSponsoredAbout(
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<ChatHelpers::Show> show,
const FullMsgId &fullId) { const FullMsgId &fullId) {
const auto session = &show->session();
show->showBox(Box([=](not_null<Ui::GenericBox*> box) { show->showBox(Box([=](not_null<Ui::GenericBox*> box) {
AboutBox(box, show, fullId); AboutBox(
box,
show,
PhrasesForMessage(fullId),
session->sponsoredMessages().lookupDetails(fullId),
session->sponsoredMessages().createReportCallback(fullId));
})); }));
} }

View file

@ -11,6 +11,11 @@ namespace ChatHelpers {
class Show; class Show;
} // namespace ChatHelpers } // namespace ChatHelpers
namespace Data {
struct SponsoredMessageDetails;
struct SponsoredReportAction;
} // namespace Data
namespace Ui { namespace Ui {
class RpWidget; class RpWidget;
namespace Menu { namespace Menu {
@ -22,6 +27,22 @@ class HistoryItem;
namespace Menu { namespace Menu {
enum class SponsoredPhrases {
Channel,
Bot,
Search,
};
void FillSponsored(
not_null<Ui::RpWidget*> parent,
const Ui::Menu::MenuCallback &addAction,
std::shared_ptr<ChatHelpers::Show> show,
SponsoredPhrases phrases,
const Data::SponsoredMessageDetails &details,
Data::SponsoredReportAction report,
bool mediaViewer,
bool skipAbout);
void FillSponsored( void FillSponsored(
not_null<Ui::RpWidget*> parent, not_null<Ui::RpWidget*> parent,
const Ui::Menu::MenuCallback &addAction, const Ui::Menu::MenuCallback &addAction,