From 611be90880be1e13fd2866a1c44c0b5bdf1ac4d0 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Fri, 31 Dec 2021 15:47:23 +0300
Subject: [PATCH] Rewrite sponsored to use fake sender names.

---
 Telegram/SourceFiles/data/data_peer.h         |  2 +
 .../data/data_sponsored_messages.cpp          | 67 ++++++++++---------
 .../data/data_sponsored_messages.h            | 21 ++++--
 Telegram/SourceFiles/data/data_types.h        |  3 -
 Telegram/SourceFiles/history/history.cpp      | 13 ++++
 Telegram/SourceFiles/history/history.h        |  5 ++
 .../history/history_inner_widget.cpp          |  2 +-
 Telegram/SourceFiles/history/history_item.cpp | 15 ++++-
 Telegram/SourceFiles/history/history_item.h   |  2 +-
 .../history/history_item_components.h         | 11 +++
 .../SourceFiles/history/history_message.cpp   | 43 +++++++++++-
 .../SourceFiles/history/history_message.h     | 10 +++
 .../SourceFiles/history/history_widget.cpp    |  4 +-
 .../history/view/history_view_element.cpp     | 64 +++++++++++-------
 .../history/view/history_view_list_widget.cpp |  2 +-
 .../history/view/history_view_message.cpp     | 48 ++++++-------
 .../history/view/history_view_view_button.cpp | 55 +++++++++------
 .../history/view/history_view_view_button.h   |  6 +-
 .../media/view/media_view_overlay_widget.cpp  |  2 +-
 19 files changed, 250 insertions(+), 125 deletions(-)

diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h
index e8f34931c..13cc7d2aa 100644
--- a/Telegram/SourceFiles/data/data_peer.h
+++ b/Telegram/SourceFiles/data/data_peer.h
@@ -42,6 +42,8 @@ class CloudImageView;
 int PeerColorIndex(PeerId peerId);
 int PeerColorIndex(BareId bareId);
 style::color PeerUserpicColor(PeerId peerId);
+
+// Must be used only for PeerColor-s.
 PeerId FakePeerIdForJustName(const QString &name);
 
 class RestrictionCheckResult {
diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.cpp b/Telegram/SourceFiles/data/data_sponsored_messages.cpp
index 6ef468191..c095c1220 100644
--- a/Telegram/SourceFiles/data/data_sponsored_messages.cpp
+++ b/Telegram/SourceFiles/data/data_sponsored_messages.cpp
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_text_entities.h"
 #include "apiwrap.h"
 #include "base/unixtime.h"
+#include "data/data_user.h"
 #include "data/data_channel.h"
 #include "data/data_peer_id.h"
 #include "data/data_session.h"
@@ -71,23 +72,10 @@ bool SponsoredMessages::append(not_null<History*> history) {
 		return false;
 	}
 
-	const auto flags = MessageFlags(0)
-		| (history->peer->isChannel() ? MessageFlag::Post : MessageFlags(0))
-		| MessageFlag::HasFromId
-		| MessageFlag::IsSponsored
-		| MessageFlag::Local;
-	auto local = history->addNewLocalMessage(
+	entryIt->item.reset(history->addNewLocalMessage(
 		_session->data().nextLocalMessageId(),
-		flags,
-		UserId(0),
-		MsgId(0),
-		HistoryItem::NewMessageDate(0),
-		entryIt->sponsored.fromId,
-		QString(),
-		entryIt->sponsored.textWithEntities,
-		MTP_messageMediaEmpty(),
-		HistoryMessageMarkupData());
-	entryIt->item.reset(std::move(local));
+		entryIt->sponsored.from,
+		entryIt->sponsored.textWithEntities));
 
 	return true;
 }
@@ -163,36 +151,49 @@ void SponsoredMessages::append(
 	});
 	const auto randomId = data.vrandom_id().v;
 	const auto hash = qs(data.vchat_invite_hash().value_or_empty());
-	const auto fromId = [&] {
+	const auto makeFrom = [](not_null<PeerData*> peer) {
+		const auto channel = peer->asChannel();
+		return SponsoredFrom{
+			.peer = peer,
+			.title = peer->name,
+			.isBroadcast = (channel && channel->isBroadcast()),
+			.isMegagroup = (channel && channel->isMegagroup()),
+			.isChannel = (channel != nullptr),
+			.isPublic = (channel && channel->isPublic()),
+			.isBot = (peer->isUser() && peer->asUser()->isBot()),
+		};
+	};
+	const auto from = [&]() -> SponsoredFrom {
 		if (data.vfrom_id()) {
-			return peerFromMTP(*data.vfrom_id());
+			return makeFrom(
+				_session->data().peer(peerFromMTP(*data.vfrom_id())));
 		}
 		Assert(data.vchat_invite());
 		return data.vchat_invite()->match([](const MTPDchatInvite &data) {
-			return Data::FakePeerIdForJustName(qs(data.vtitle()));
+			return SponsoredFrom{
+				.title = qs(data.vtitle()),
+				.isBroadcast = data.is_broadcast(),
+				.isMegagroup = data.is_megagroup(),
+				.isChannel = data.is_channel(),
+				.isPublic = data.is_public(),
+			};
 		}, [&](const MTPDchatInviteAlready &data) {
 			const auto chat = _session->data().processChat(data.vchat());
-			if (!chat) {
-				return PeerId(0);
-			}
 			if (const auto channel = chat->asChannel()) {
 				channel->clearInvitePeek();
 			}
-			return chat->id;
+			return makeFrom(chat);
 		}, [&](const MTPDchatInvitePeek &data) {
 			const auto chat = _session->data().processChat(data.vchat());
-			if (!chat) {
-				return PeerId(0);
-			}
 			if (const auto channel = chat->asChannel()) {
 				channel->setInvitePeek(hash, data.vexpires().v);
 			}
-			return chat->id;
+			return makeFrom(chat);
 		});
 	}();
 	auto sharedMessage = SponsoredMessage{
 		.randomId = randomId,
-		.fromId = fromId,
+		.from = from,
 		.textWithEntities = {
 			.text = qs(data.vmessage()),
 			.entities = Api::EntitiesFromMTP(
@@ -263,17 +264,17 @@ void SponsoredMessages::view(const FullMsgId &fullId) {
 	}).send();
 }
 
-SponsoredMessages::ChannelPost SponsoredMessages::channelPost(
+SponsoredMessages::Details SponsoredMessages::lookupDetails(
 		const FullMsgId &fullId) const {
 	const auto entryPtr = find(fullId);
 	if (!entryPtr) {
-		return { .msgId = ShowAtUnreadMsgId, .hash = std::nullopt };
+		return {};
 	}
-	const auto msgId = entryPtr->sponsored.msgId;
-	const auto hash = entryPtr->sponsored.chatInviteHash;
+	const auto &hash = entryPtr->sponsored.chatInviteHash;
 	return {
-		.msgId = msgId ? msgId : ShowAtUnreadMsgId,
 		.hash = hash.isEmpty() ? std::nullopt : std::make_optional(hash),
+		.peer = entryPtr->sponsored.from.peer,
+		.msgId = entryPtr->sponsored.msgId,
 	};
 }
 
diff --git a/Telegram/SourceFiles/data/data_sponsored_messages.h b/Telegram/SourceFiles/data/data_sponsored_messages.h
index 90c2a4ef0..5e3109feb 100644
--- a/Telegram/SourceFiles/data/data_sponsored_messages.h
+++ b/Telegram/SourceFiles/data/data_sponsored_messages.h
@@ -20,9 +20,19 @@ namespace Data {
 
 class Session;
 
-struct SponsoredMessage final {
+struct SponsoredFrom {
+	PeerData *peer = nullptr;
+	QString title;
+	bool isBroadcast = false;
+	bool isMegagroup = false;
+	bool isChannel = false;
+	bool isPublic = false;
+	bool isBot = false;
+};
+
+struct SponsoredMessage {
 	QByteArray randomId;
-	PeerId fromId;
+	SponsoredFrom from;
 	TextWithEntities textWithEntities;
 	History *history = nullptr;
 	MsgId msgId;
@@ -31,9 +41,10 @@ struct SponsoredMessage final {
 
 class SponsoredMessages final {
 public:
-	struct ChannelPost {
-		MsgId msgId;
+	struct Details {
 		std::optional<QString> hash;
+		PeerData *peer = nullptr;
+		MsgId msgId;
 	};
 	using RandomId = QByteArray;
 	explicit SponsoredMessages(not_null<Session*> owner);
@@ -45,7 +56,7 @@ public:
 	void request(not_null<History*> history);
 	[[nodiscard]] bool append(not_null<History*> history);
 	void clearItems(not_null<History*> history);
-	[[nodiscard]] ChannelPost channelPost(const FullMsgId &fullId) const;
+	[[nodiscard]] Details lookupDetails(const FullMsgId &fullId) const;
 
 	void view(const FullMsgId &fullId);
 
diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h
index 4c3c5629d..77e42ae9a 100644
--- a/Telegram/SourceFiles/data/data_types.h
+++ b/Telegram/SourceFiles/data/data_types.h
@@ -280,9 +280,6 @@ enum class MessageFlag : uint32 {
 
 	// Contact sign-up message, notification should be skipped for Silent.
 	IsContactSignUp       = (1U << 30),
-
-	// In channels.
-	IsSponsored           = (1U << 31),
 };
 inline constexpr bool is_flag_type(MessageFlag) { return true; }
 using MessageFlags = base::flags<MessageFlag>;
diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp
index b951587ee..614ebac0c 100644
--- a/Telegram/SourceFiles/history/history.cpp
+++ b/Telegram/SourceFiles/history/history.cpp
@@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_changes.h"
 #include "data/data_chat_filters.h"
 #include "data/data_scheduled_messages.h"
+#include "data/data_sponsored_messages.h"
 #include "data/data_send_action.h"
 #include "data/data_folder.h"
 #include "data/data_photo.h"
@@ -673,6 +674,18 @@ not_null<HistoryItem*> History::addNewLocalMessage(
 		true);
 }
 
+not_null<HistoryItem*> History::addNewLocalMessage(
+		MsgId id,
+		Data::SponsoredFrom from,
+		const TextWithEntities &textWithEntities) {
+	return addNewItem(
+		makeMessage(
+			id,
+			from,
+			textWithEntities),
+		true);
+}
+
 void History::setUnreadMentionsCount(int count) {
 	const auto had = _unreadMentionsCount && (*_unreadMentionsCount > 0);
 	if (_unreadMentions.size() > count) {
diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h
index a3f4178c7..f70d0ba79 100644
--- a/Telegram/SourceFiles/history/history.h
+++ b/Telegram/SourceFiles/history/history.h
@@ -35,6 +35,7 @@ struct Draft;
 class Session;
 class Folder;
 class ChatFilter;
+struct SponsoredFrom;
 
 enum class ForwardOptions {
 	PreserveInfo,
@@ -190,6 +191,10 @@ public:
 		const QString &postAuthor,
 		not_null<GameData*> game,
 		HistoryMessageMarkupData &&markup);
+	not_null<HistoryItem*> addNewLocalMessage(
+		MsgId id,
+		Data::SponsoredFrom from,
+		const TextWithEntities &textWithEntities); // sponsored
 
 	// Used only internally and for channel admin log.
 	not_null<HistoryItem*> createItem(
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index 37a6aef07..adc27e192 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -896,7 +896,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
 							userpicTop,
 							width(),
 							st::msgPhotoSize);
-					} else if (const auto info = view->data()->hiddenForwardedInfo()) {
+					} else if (const auto info = view->data()->hiddenSenderInfo()) {
 						info->userpic.paint(
 							p,
 							st::historyPhotoLeft,
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index d85a8641f..31e866ffa 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_channel.h"
 #include "data/data_chat.h"
 #include "data/data_user.h"
+#include "data/data_sponsored_messages.h"
 #include "styles/style_dialogs.h"
 #include "styles/style_chat.h"
 
@@ -294,6 +295,10 @@ HistoryItem *HistoryItem::lookupDiscussionPostOriginal() const {
 PeerData *HistoryItem::displayFrom() const {
 	if (const auto sender = discussionPostOriginalSender()) {
 		return sender;
+	} else if (const auto sponsored = Get<HistoryMessageSponsored>()) {
+		if (sponsored->sender) {
+			return nullptr;
+		}
 	} else if (const auto forwarded = Get<HistoryMessageForwarded>()) {
 		if (history()->peer->isSelf() || history()->peer->isRepliesChat() || forwarded->imported) {
 			return forwarded->originalSender;
@@ -484,7 +489,7 @@ bool HistoryItem::isScheduled() const {
 }
 
 bool HistoryItem::isSponsored() const {
-	return (_flags & MessageFlag::IsSponsored);
+	return Has<HistoryMessageSponsored>();
 }
 
 bool HistoryItem::skipNotification() const {
@@ -900,8 +905,10 @@ PeerData *HistoryItem::senderOriginal() const {
 	return (peer->isChannel() && !peer->isMegagroup()) ? peer : from();
 }
 
-const HiddenSenderInfo *HistoryItem::hiddenForwardedInfo() const {
-	if (const auto forwarded = Get<HistoryMessageForwarded>()) {
+const HiddenSenderInfo *HistoryItem::hiddenSenderInfo() const {
+	if (const auto sponsored = Get<HistoryMessageSponsored>()) {
+		return sponsored->sender.get();
+	} else if (const auto forwarded = Get<HistoryMessageForwarded>()) {
 		return forwarded->hiddenSenderInfo.get();
 	}
 	return nullptr;
@@ -1118,6 +1125,8 @@ ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const {
 	const auto sender = [&]() -> std::optional<QString> {
 		if (options.hideSender || isPost() || isEmpty()) {
 			return {};
+		} else if (const auto sponsored = Get<HistoryMessageSponsored>()) {
+			return sponsored->sender->name;
 		} else if (!_history->peer->isUser()) {
 			if (const auto from = displayFrom()) {
 				return fromSender(from);
diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h
index a2f447be1..dae4f898d 100644
--- a/Telegram/SourceFiles/history/history_item.h
+++ b/Telegram/SourceFiles/history/history_item.h
@@ -387,7 +387,7 @@ public:
 
 	[[nodiscard]] TimeId dateOriginal() const;
 	[[nodiscard]] PeerData *senderOriginal() const;
-	[[nodiscard]] const HiddenSenderInfo *hiddenForwardedInfo() const;
+	[[nodiscard]] const HiddenSenderInfo *hiddenSenderInfo() const;
 	[[nodiscard]] not_null<PeerData*> fromOriginal() const;
 	[[nodiscard]] QString authorOriginal() const;
 	[[nodiscard]] MsgId idOriginal() const;
diff --git a/Telegram/SourceFiles/history/history_item_components.h b/Telegram/SourceFiles/history/history_item_components.h
index bf0ed95f6..87021780b 100644
--- a/Telegram/SourceFiles/history/history_item_components.h
+++ b/Telegram/SourceFiles/history/history_item_components.h
@@ -102,6 +102,17 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
 	bool imported = false;
 };
 
+struct HistoryMessageSponsored : public RuntimeComponent<HistoryMessageSponsored, HistoryItem> {
+	enum class Type : uchar {
+		User,
+		Group,
+		Broadcast,
+		Bot,
+	};
+	std::unique_ptr<HiddenSenderInfo> sender;
+	Type type = Type::User;
+};
+
 struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> {
 	HistoryMessageReply() = default;
 	HistoryMessageReply(const HistoryMessageReply &other) = delete;
diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp
index 9063207bc..0634657eb 100644
--- a/Telegram/SourceFiles/history/history_message.cpp
+++ b/Telegram/SourceFiles/history/history_message.cpp
@@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_user.h"
 #include "data/data_histories.h"
 #include "data/data_web_page.h"
+#include "data/data_sponsored_messages.h"
 #include "styles/style_dialogs.h"
 #include "styles/style_widgets.h"
 #include "styles/style_chat.h"
@@ -582,7 +583,7 @@ HistoryMessage::HistoryMessage(
 			&& (!originalMedia || !originalMedia->forceForwardedInfo()));
 	if (!dropForwardInfo) {
 		config.originalDate = original->dateOriginal();
-		if (const auto info = original->hiddenForwardedInfo()) {
+		if (const auto info = original->hiddenSenderInfo()) {
 			config.senderNameOriginal = info->name;
 		} else if (const auto senderOriginal = original->senderOriginal()) {
 			config.senderOriginal = senderOriginal->id;
@@ -768,6 +769,29 @@ HistoryMessage::HistoryMessage(
 	setEmptyText();
 }
 
+HistoryMessage::HistoryMessage(
+	not_null<History*> history,
+	MsgId id,
+	Data::SponsoredFrom from,
+	const TextWithEntities &textWithEntities)
+: HistoryItem(
+		history,
+		id,
+		((history->peer->isChannel() ? MessageFlag::Post : MessageFlag(0))
+			//| (from.peer ? MessageFlag::HasFromId : MessageFlag(0))
+			| MessageFlag::Local),
+		HistoryItem::NewMessageDate(0),
+		/*from.peer ? from.peer->id : */PeerId(0)) {
+	createComponentsHelper(
+		_flags,
+		MsgId(0), // replyTo
+		UserId(0), // viaBotId
+		QString(), // postAuthor
+		HistoryMessageMarkupData());
+	setText(textWithEntities);
+	setSponsoredFrom(from);
+}
+
 void HistoryMessage::createComponentsHelper(
 		MessageFlags flags,
 		MsgId replyTo,
@@ -1915,6 +1939,23 @@ void HistoryMessage::setUnreadRepliesCount(
 		Data::MessageUpdate::Flag::RepliesUnreadCount);
 }
 
+void HistoryMessage::setSponsoredFrom(const Data::SponsoredFrom &from) {
+	AddComponents(HistoryMessageSponsored::Bit());
+	const auto sponsored = Get<HistoryMessageSponsored>();
+	sponsored->sender = std::make_unique<HiddenSenderInfo>(
+		from.title,
+		false);
+
+	using Type = HistoryMessageSponsored::Type;
+	sponsored->type = from.isBot
+		? Type::Bot
+		: from.isBroadcast
+		? Type::Broadcast
+		: (from.peer && from.peer->isUser())
+		? Type::User
+		: Type::Group;
+}
+
 void HistoryMessage::setReplyToTop(MsgId replyToTop) {
 	const auto reply = Get<HistoryMessageReply>();
 	if (!reply
diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h
index 3cf8c8ec4..eb02f0906 100644
--- a/Telegram/SourceFiles/history/history_message.h
+++ b/Telegram/SourceFiles/history/history_message.h
@@ -14,6 +14,10 @@ struct SendAction;
 struct SendOptions;
 } // namespace Api
 
+namespace Data {
+struct SponsoredFrom;
+} // namespace Data
+
 namespace HistoryView {
 class Message;
 } // namespace HistoryView
@@ -115,6 +119,11 @@ public:
 		const QString &postAuthor,
 		not_null<GameData*> game,
 		HistoryMessageMarkupData &&markup); // local game
+	HistoryMessage(
+		not_null<History*> history,
+		MsgId id,
+		Data::SponsoredFrom from,
+		const TextWithEntities &textWithEntities); // sponsored
 
 	void refreshMedia(const MTPMessageMedia *media);
 	void refreshSentMedia(const MTPMessageMedia *media);
@@ -251,6 +260,7 @@ private:
 	void setUnreadRepliesCount(
 		not_null<HistoryMessageViews*> views,
 		int count);
+	void setSponsoredFrom(const Data::SponsoredFrom &from);
 
 	static void FillForwardedInfo(
 		CreateConfig &config,
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 8a6dcd112..f3d85ffff 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -7006,7 +7006,7 @@ void HistoryWidget::updateForwardingTexts() {
 					fullname = from->name;
 				}
 				version += from->nameVersion;
-			} else if (const auto info = item->hiddenForwardedInfo()) {
+			} else if (const auto info = item->hiddenSenderInfo()) {
 				if (!insertedNames.contains(info->name)) {
 					insertedNames.emplace(info->name);
 					names.push_back(info->firstName);
@@ -7058,7 +7058,7 @@ void HistoryWidget::checkForwardingInfo() {
 			for (const auto item : _toForward.items) {
 				if (const auto from = item->senderOriginal()) {
 					version += from->nameVersion;
-				} else if (const auto info = item->hiddenForwardedInfo()) {
+				} else if (const auto info = item->hiddenSenderInfo()) {
 					++version;
 				} else {
 					Unexpected("Corrupt forwarded information in message.");
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index a188b342c..97058d6c4 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -45,7 +45,7 @@ namespace {
 // A new message from the same sender is attached to previous within 15 minutes.
 constexpr int kAttachMessageToPreviousSecondsDelta = 900;
 
-bool IsAttachedToPreviousInSavedMessages(
+[[nodiscard]] bool IsAttachedToPreviousInSavedMessages(
 		not_null<HistoryItem*> previous,
 		HistoryMessageForwarded *prevForwarded,
 		not_null<HistoryItem*> item,
@@ -65,6 +65,22 @@ bool IsAttachedToPreviousInSavedMessages(
 	return (*previousInfo == *itemInfo);
 }
 
+[[nodiscard]] Window::SessionController *ContextOrSessionWindow(
+		const ClickHandlerContext &context,
+		not_null<Main::Session*> session) {
+	if (const auto controller = context.sessionWindow.get()) {
+		return controller;
+	}
+	const auto &windows = session->windows();
+	if (windows.empty()) {
+		session->domain().activate(&session->account());
+		if (windows.empty()) {
+			return nullptr;
+		}
+	}
+	return windows.front();
+}
+
 } // namespace
 
 std::unique_ptr<Ui::PathShiftGradient> MakePathShiftGradient(
@@ -599,7 +615,26 @@ ClickHandlerPtr Element::fromLink() const {
 		return _fromLink;
 	}
 	const auto item = data();
-	if (const auto from = item->displayFrom()) {
+	if (item->isSponsored()) {
+		const auto session = &item->history()->session();
+		_fromLink = std::make_shared<LambdaClickHandler>([=](
+				ClickContext context) {
+			if (context.button != Qt::LeftButton) {
+				return;
+			}
+			const auto my = context.other.value<ClickHandlerContext>();
+			if (const auto window = ContextOrSessionWindow(my, session)) {
+				auto &sponsored = session->data().sponsoredMessages();
+				const auto details = sponsored.lookupDetails(my.itemId);
+				if (const auto &hash = details.hash) {
+					Api::CheckChatInvite(window, *hash);
+				} else if (const auto peer = details.peer) {
+					window->showPeerInfo(peer);
+				}
+			}
+		});
+		return _fromLink;
+	} else if (const auto from = item->displayFrom()) {
 		_fromLink = std::make_shared<LambdaClickHandler>([=](
 				ClickContext context) {
 			if (context.button != Qt::LeftButton) {
@@ -607,29 +642,8 @@ ClickHandlerPtr Element::fromLink() const {
 			}
 			const auto my = context.other.value<ClickHandlerContext>();
 			const auto session = &from->session();
-			const auto window = [&]() -> Window::SessionController* {
-				if (const auto controller = my.sessionWindow.get()) {
-					return controller;
-				}
-				const auto &windows = session->windows();
-				if (windows.empty()) {
-					session->domain().activate(&session->account());
-					if (windows.empty()) {
-						return nullptr;
-					}
-				}
-				return windows.front();
-			}();
-			if (window) {
-				const auto inviteHash = item->isSponsored()
-					? session->data().sponsoredMessages().channelPost(
-						my.itemId).hash
-					: std::nullopt;
-				if (inviteHash) {
-					Api::CheckChatInvite(window, *inviteHash);
-				} else {
-					window->showPeerInfo(from);
-				}
+			if (const auto window = ContextOrSessionWindow(my, session)) {
+				window->showPeerInfo(from);
 			}
 		});
 		_fromLink->setProperty(kPeerLinkPeerIdProperty, from->id.value);
diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
index 29e2bdbe9..3cfb449ee 100644
--- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp
@@ -1753,7 +1753,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
 						userpicTop,
 						view->width(),
 						st::msgPhotoSize);
-				} else if (const auto info = view->data()->hiddenForwardedInfo()) {
+				} else if (const auto info = view->data()->hiddenSenderInfo()) {
 					info->userpic.paint(
 						p,
 						st::historyPhotoLeft,
diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index d00c33b29..151a2ab96 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_user.h"
 #include "data/data_channel.h"
 #include "data/data_message_reactions.h"
+#include "data/data_sponsored_messages.h"
 #include "lang/lang_keys.h"
 #include "mainwidget.h"
 #include "main/main_session.h"
@@ -415,7 +416,7 @@ QSize Message::performCountOptimalSize() {
 				const auto from = item->displayFrom();
 				const auto &name = from
 					? from->nameText()
-					: item->hiddenForwardedInfo()->nameText;
+					: item->hiddenSenderInfo()->nameText;
 				auto namew = st::msgPadding.left()
 					+ name.maxWidth()
 					+ st::msgPadding.right();
@@ -935,17 +936,19 @@ void Message::paintFromName(
 
 	const auto nameText = [&]() -> const Ui::Text::String * {
 		const auto from = item->displayFrom();
-		if (context.outbg || item->isPost()) {
-			p.setPen(stm->msgServiceFg);
+		const auto service = (context.outbg || item->isPost());
+		if (from) {
+			p.setPen(service
+				? stm->msgServiceFg
+				: FromNameFg(context, from->id));
 			return &from->nameText();
-		} else if (from) {
-			p.setPen(FromNameFg(context, from->id));
-			return &from->nameText();
-		} else if (const auto info = item->hiddenForwardedInfo()) {
-			p.setPen(FromNameFg(context, info->colorPeerId));
+		} else if (const auto info = item->hiddenSenderInfo()) {
+			p.setPen(service
+				? stm->msgServiceFg
+				: FromNameFg(context, info->colorPeerId));
 			return &info->nameText;
 		} else {
-			Unexpected("Corrupt forwarded information in message.");
+			Unexpected("Corrupt sender information in message.");
 		}
 	}();
 	nameText->drawElided(p, availableLeft, trect.top(), availableWidth);
@@ -1524,7 +1527,7 @@ bool Message::getStateFromName(
 			const auto nameText = [&]() -> const Ui::Text::String * {
 				if (from) {
 					return &from->nameText();
-				} else if (const auto info = item->hiddenForwardedInfo()) {
+				} else if (const auto info = item->hiddenSenderInfo()) {
 					return &info->nameText;
 				} else {
 					Unexpected("Corrupt forwarded information in message.");
@@ -2076,14 +2079,11 @@ int Message::viewButtonHeight() const {
 }
 
 void Message::updateViewButtonExistence() {
-	const auto has = [&] {
-		const auto item = data();
-		if (item->isSponsored()) {
-			return true;
-		}
-		const auto media = item->media();
-		return media && ViewButton::MediaHasViewButton(media);
-	}();
+	const auto item = data();
+	const auto sponsored = item->Get<HistoryMessageSponsored>();
+	const auto media = sponsored ? nullptr : item->media();
+	const auto has = sponsored
+		|| (media && ViewButton::MediaHasViewButton(media));
 	if (!has) {
 		_viewButton = nullptr;
 		return;
@@ -2091,13 +2091,9 @@ void Message::updateViewButtonExistence() {
 		return;
 	}
 	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));
+	_viewButton = sponsored
+		? std::make_unique<ViewButton>(sponsored, std::move(callback))
+		: std::make_unique<ViewButton>(media, std::move(callback));
 }
 
 void Message::initLogEntryOriginal() {
@@ -2572,7 +2568,7 @@ void Message::fromNameUpdated(int width) const {
 			const auto nameText = [&]() -> const Ui::Text::String * {
 				if (from) {
 					return &from->nameText();
-				} else if (const auto info = item->hiddenForwardedInfo()) {
+				} else if (const auto info = item->hiddenSenderInfo()) {
 					return &info->nameText;
 				} else {
 					Unexpected("Corrupted forwarded information in message.");
diff --git a/Telegram/SourceFiles/history/view/history_view_view_button.cpp b/Telegram/SourceFiles/history/view/history_view_view_button.cpp
index 3fd4f8a42..783b0878c 100644
--- a/Telegram/SourceFiles/history/view/history_view_view_button.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_view_button.cpp
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_sponsored_messages.h"
 #include "data/data_user.h"
 #include "data/data_web_page.h"
+#include "history/history_item_components.h"
 #include "history/view/history_view_cursor_state.h"
 #include "lang/lang_keys.h"
 #include "main/main_session.h"
@@ -30,20 +31,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace HistoryView {
 namespace {
 
-inline auto PeerToPhrase(not_null<PeerData*> peer) {
+using SponsoredType = HistoryMessageSponsored::Type;
+
+inline auto SponsoredPhrase(SponsoredType type) {
 	const auto phrase = [&] {
-		if (const auto user = peer->asUser()) {
-			return user->isBot()
-				? tr::lng_view_button_bot
-				: tr::lng_view_button_user;
-		} else if (peer->isChat()) {
-			return tr::lng_view_button_group;
-		} else if (peer->isChannel()) {
-			return tr::lng_view_button_channel;
+		switch (type) {
+		case SponsoredType::Bot: return tr::lng_view_button_bot;
+		case SponsoredType::Group: return tr::lng_view_button_group;
+		case SponsoredType::Broadcast: return tr::lng_view_button_channel;
+		case SponsoredType::User: return tr::lng_view_button_user;
 		}
-		Unexpected("Invalid peer in ViewButton.");
-	}()(tr::now);
-	return Ui::Text::Upper(phrase);
+		Unexpected("SponsoredType in SponsoredPhrase.");
+	}();
+	return Ui::Text::Upper(phrase(tr::now));
 }
 
 inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
@@ -75,8 +75,11 @@ inline auto WebPageToPhrase(not_null<WebPageData*> webpage) {
 } // namespace
 
 struct ViewButton::Inner {
-	Inner(not_null<PeerData*> peer, Fn<void()> updateCallback);
+	Inner(
+		not_null<HistoryMessageSponsored*> sponsored,
+		Fn<void()> updateCallback);
 	Inner(not_null<Data::Media*> media, Fn<void()> updateCallback);
+
 	void updateMask(int height);
 	void toggleRipple(bool pressed);
 
@@ -114,22 +117,28 @@ bool ViewButton::MediaHasViewButton(
 			&& webpage->document->isWallPaper());
 }
 
-ViewButton::Inner::Inner(not_null<PeerData*> peer, Fn<void()> updateCallback)
+ViewButton::Inner::Inner(
+	not_null<HistoryMessageSponsored*> sponsored,
+	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 link = data.sponsoredMessages().channelPost(my.itemId);
-		if (link.hash) {
-			Api::CheckChatInvite(controller, *link.hash);
-		} else {
-			controller->showPeer(peer, link.msgId);
+		const auto itemId = my.itemId;
+		const auto details = data.sponsoredMessages().lookupDetails(itemId);
+		if (details.hash) {
+			Api::CheckChatInvite(controller, *details.hash);
+		} else if (details.peer) {
+			controller->showPeerHistory(
+				details.peer,
+				Window::SectionShow::Way::Forward,
+				details.msgId);
 		}
 	}
 }))
 , updateCallback(std::move(updateCallback))
-, text(st::historyViewButtonTextStyle, PeerToPhrase(peer)) {
+, text(st::historyViewButtonTextStyle, SponsoredPhrase(sponsored->type)) {
 }
 
 ViewButton::Inner::Inner(
@@ -170,8 +179,10 @@ void ViewButton::Inner::toggleRipple(bool pressed) {
 	}
 }
 
-ViewButton::ViewButton(not_null<PeerData*> peer, Fn<void()> updateCallback)
-: _inner(std::make_unique<Inner>(peer, std::move(updateCallback))) {
+ViewButton::ViewButton(
+	not_null<HistoryMessageSponsored*> sponsored,
+	Fn<void()> updateCallback)
+: _inner(std::make_unique<Inner>(sponsored, std::move(updateCallback))) {
 }
 
 ViewButton::ViewButton(
diff --git a/Telegram/SourceFiles/history/view/history_view_view_button.h b/Telegram/SourceFiles/history/view/history_view_view_button.h
index f49ae2ab2..d9b45b7f8 100644
--- a/Telegram/SourceFiles/history/view/history_view_view_button.h
+++ b/Telegram/SourceFiles/history/view/history_view_view_button.h
@@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "ui/chat/chat_style.h"
 
+struct HistoryMessageSponsored;
+
 namespace Data {
 class Media;
 } // namespace Data
@@ -21,7 +23,9 @@ struct TextState;
 
 class ViewButton {
 public:
-	ViewButton(not_null<PeerData*> peer, Fn<void()> updateCallback);
+	ViewButton(
+		not_null<HistoryMessageSponsored*> sponsored,
+		Fn<void()> updateCallback);
 	ViewButton(not_null<Data::Media*> media, Fn<void()> updateCallback);
 	~ViewButton();
 
diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
index a63f49839..8e343df24 100644
--- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
+++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp
@@ -2130,7 +2130,7 @@ void OverlayWidget::refreshMediaViewer() {
 void OverlayWidget::refreshFromLabel() {
 	if (_message) {
 		_from = _message->senderOriginal();
-		if (const auto info = _message->hiddenForwardedInfo()) {
+		if (const auto info = _message->hiddenSenderInfo()) {
 			_fromName = info->name;
 		} else {
 			Assert(_from != nullptr);